(Continued from BusinessLogicDefinitionDiscussion)
This is a description of a sample sales lead tracking system for a hypothetical civil engineering firm. It's a "composite character" of some apps I've worked on. The organization hears about potential projects (customers with needs) and the marketing team contacts them to ask questions and butter them up.
Draft schema:
projects (table)
-------
projID
orgRef // foreign key to Orgs, "ref" means FK
title
descript
status // open, proposal, accepted, rejected, other, cancelled
whenDiscovered // date
notes
orgs (table)
-----------
orgID
title
orgType
contact // see PartyPattern for fancier address/contact
notes
persons (table)
---------
personID
orgRef
name
position
contact
notes
meetups (table) // AKA "encounters"
----------
meetID
meetType // phone, restaurant, our-place, their-place, etc.
statusRate // status (planned, etc.) or ranking: A to F of "feeling" of meeting
when // date/time
where
duration // in hours or portion of
notes
meetupAssoc (table) // Meetup assocatiations (many-to-many)
----------------
meetRef
personRef
notes
Yes, most of such would be run-of-the-mill CRUD and reporting, but usually various "business rules" creep in over time, such as: send email notices for open projects that have had no contact for more than 4 months.
--Mr. Top
This is classic data entry and reporting. There's good coin to be made churning these out, and building them successfully requires extensive general business and domain knowledge and analysis skills, but they're almost entirely presentation logic: The essence is that data goes in at one end and is reported at the other. Business logic -- which computes and records costs, taxes, schedules, timetables, bills, routes, plans, payrolls, etc. -- is generally absent.
But "computing taxes" affects output also. If nobody sees the computation results then it doesn't need to be done. Just about any app is like that: the user only ultimately cares about the output because they don't "consume" the internal workings. I see no useful reason to make a distinction between logic that adds two numbers together versus logic that uses the value of one number to determine if the second one is displayed under a given circumstance, for example. Again, you are giving Boolean computations a lower-class (or different class?) status than numeric computations. Partitioning by calculation result value type on a large scale is utterly silly and arbitrary; of no discernible economic value. Would you partition the app in two parts, one by integer calculations and one by real/double computations???
Further, reports often do sum and average information. True, it usually doesn't "save" such, but you guys have previously said that "persistence" is not the difference maker (plus, it's continuous also). You need to formulate more concise rules beyond "I know it when I see it because I'm [allegedly] smart and experienced". -t
Perhaps I can illustrate (or not -- it depends on your answer) via the simplest possible example. Which is the better code? This...
writeln(a + b)
...or this?
r = a + b
writeln(r)
Be sure to explain why you answer the way you do.
ItDepends. Remember, I use estimated maintenance patterns along with frequency times costs to make most of such decisions. I would need to know more about the context to make such estimates.
And, let's try to separate definition issues from "good design" issues, if possible.
I have no further explanations that can help. Those who argue that the latter example is better code will appreciate the distinction between business logic and presentation logic, and why it exists. Those who argue that ItDepends, will not.
That's not very helpful text for those of us who allegedly "don't get it". Is WesternReductionism dead?
No, but I've got better things to do than debate this.
All else being equal, less code is better than more code, and the second one is more code. However, the WetWare of most humans works better when big processes are sub-divided into smaller sub-processes to some extent: too fine a ratio can be just as bad as no division. (See DivideAndConquer) Finding the right balance (division ratio) comes from experience and from estimating how maintainer WetWare will likely work, and likely future ChangePatterns of the domain requests. (In other words, how likely is a domain need going to come along to make use of a smaller sub-division.) In summary, we have two concerns: "Readability" of the code as-is, and second, will division make future changes easier to make.
A third concern is that the second is probably easier to debug using most existing debuggers. But it may not be practical to write every block/section under the assumption that every one will need to be debugged in the debugger; only a small fraction will. If the chance is large, then it's crappy code and should be inspected more closely before testing via a run.
In short, the second example is better code, unconditionally. It's better code because the first line -- which exemplifies the essence of business logic -- is separate from the second line, which exemplifies presentation logic. We can thus change business logic independently of presentation logic, and change presentation logic independently of business logic. Being able to change components independently is always good.
Obviously, this is a trivial example, but the underlying concept applies to arbitrarily-complex systems. Conflating business logic with presentation logic -- exemplified by the first example -- complicates maintenance, development, testing and debugging regardless of WetWare.
ChangePatterns often don't fit our ideal pre-conceived notions or classifications. Over time one gets a "feel" for what kind of changes are likely, but often they vary on a wide variety of aspects/dimensions. Thus, "change independently" is a somewhat elusive target. In general I agree with designing/partitioning around ChangePattern's. I just disagree that "presentation" versus "business logic" is sufficiently clear and a good (change-fitting) separation criteria.
For example, a given "aspect" of business may have a presentation and non-presentation aspect to it (assuming there is such a thing for the sake of argument). The change is on the aspect itself, not the presentation side or the non-presentation side. Let's say it's an activation of a feature. If the feature is activated, then it affects both the presentation side and the non-presentation-side. To make it make it easier to switch on and off, we partition by the feature, NOT it's presentation-ness:
// feature X
if (feature_X_IsOn) then
event foo(...) // register an event handler
presentationStuff(...)
end event
event bar(...)
nonPresentationStuff(...)
end event
end if
- [Why did you split this example into presentationStuff() and nonPresentationStuff() if you're trying to demonstrate a case under which one wouldn't split presentational logic from business logic? -DavidMcLean?]
- Perhaps we need to clarify "split". "Separation" is continuous. I generally assumed each event/function/object would be used at different times, but this was not the main point of the example otherwise. The devil's in the details.
- [You have two functions in your example, presentationStuff() and nonPresentationStuff(). They're quite clearly separate functions. One presumes that presentationStuff() contains presentational logic and that nonPresentationStuff() contains business logic, therefore your above snippet is in fact an example of separating business logic from presentation logic. -DavidMcLean?]
- Like I've said before, sometimes I separate and sometimes I don't. ItDepends. I didn't say "never separate". And, is a function sufficient "separation"? Neither "business logic" nor "separation" is sufficiently defined (for the context) after 3 topics on this. I'm growing impatient and frustrated with this discussion. "I know biz logic & sep when I see it because I'm smart and experienced" is nearly useless to readers, even if it was true.
- [Bereft of context, it's unclear whether defining presentationStuff() and nonPresentationStuff() as separate functions is in fact sufficient separation. However, it absolutely satisfies the fundamental requirement that business and presentational logic are delimited from one another and kept separate in some fashion. For example, the nonPresentationStuff() will be accessible to other calculations without dragging in the presentationStuff() since the two may be called independently of each other (as they are in your example, in fact). It's unclear where those two functions have been defined, though: If the project's big enough for multiple modules, then presentationStuff() and nonPresentationStuff() might best also be defined in separate modules. From a ModelViewController perspective, the presentationStuff() belongs in some view or another, while nonPresentationStuff() is probably part of a model (models and views generally live in separate modules). -DavidMcLean?]
- Sometimes forcing things to be separate requires reinventing a lot of necessarily contextual information, leading to a OnceAndOnlyOnce violation and/or bloated interface coding. The devil's in the details and the separation question is situational. I'm not going to add bloat JUST to fit some unscientific faddish idealism. I'll go with the simpler design unless there is likely to be a future ChangePattern that would benefit from separating something.
- ["Forcing" things to be separate? You made them separate for your example, while trying to demonstrate a situation in which you wouldn't want to separate them. Wouldn't the appropriate conclusion there be that separating business from presentational logic is a perfectly natural thing to do? -DavidMcLean?]
- No. I just made them functions because I didn't want to give detail code in the example. And how can it be "natural" when you cannot even define the buggers?
- [So in a real application you'd make the explicit choice not to have separate functions? Why? As for definition, BusinessLogicDefinition defined them, right at the top. -DavidMcLean?]
- No, I have said multiple times ItDepends. And the definition is tied to other vague words.
- [Why does it depend? Under what circumstances is it not of value to keep business logic separate from presentation? -DavidMcLean?]
- Use SimulationOfTheFuture to find out. It depends because we are weighing multiple (often competing) factors against each other, and in some circumstances one factor may overpower another. SoftwareEngineeringIsArtOfCompromise. If your SOC choice is a lot of extra code but not likely to align with future changes and/or makes the code harder to read, I'll probably skip it, for example. [[Added later.]]
- [Right, because unlike our definitions that's not vague at all. -DavidMcLean?]
- You mean estimations are rough? True, but that's because working crystal balls have not been invented yet, so we use the next best alternatives, largely borrowed from the financial industry. -t
- [No, I mean "use SimulationOfTheFuture to find out" is hardly identifying any particular circumstance under which intertwining business and presentational logic is more valuable than keeping them separate. I'm simulating the future right now, and I can only see situations in which separating those concerns is better. If you think otherwise, please actually identify a situation. -DavidMcLean?]
- Fine, take a sample app and show me via specific code where and why the bad things happen in the future.
- [You just shifted the burden of proof, so I'll shift it back. Where and why is it valuable not to separate business logic from presentation? -DavidMcLean?]
- Why is the default "to separate"? YagNi says otherwise. We went over some situations in BusinessLogicDefinitionDiscussion, and each side disagreed about the odds. I don't know what to say other than without more careful and objective studies, such as saving edit sessions of real projects, we each only have our experience to say X is more likely than Y.
- [YagNi doesn't say anything about code structure. All it says is that you shouldn't work to add a feature unless you're sure that feature is needed, such as by having a UserStory about it. Business and presentation logic should be separated for much the same reasons all SeparationOfConcerns is applied: It's a lesser cognitive load to develop one concern at a time, aspects are more interchangeable (for instance, one might swap out a desktop GUI for an HTML one without touching the business logic), unit testing is more possible as concerns are more isolated as units, and maintenance is simplified as errors also are isolated and concerns may be maintained independently. There's no particular reasons in support of not separating such concerns, as far as I'm aware; feel free to identify a scenario that demonstrates otherwise. -DavidMcLean?]
- YagNi does too say something about code structure: Don't add it unless it's needed. I'm not against some future-proofing, but am merely pointing out there are common counter-principles out there that contradict biz/pres separation.
- [YagNi only applies to features---you aren't going to need a feature unless you have a UserStory/UseCase/something to show that you do need it. There is no equivalent of YagNi for code structure principles, and it's not in conflict with YagNi to apply SeparationOfConcerns, because SeparationOfConcerns is not a feature. -DavidMcLean?]
- How do you surmise that scope limitation?
- That is the recognised definition of YagNi.
- How do you recon that exactly? It's not how I interpret it. Ron Jeffries used the word "things", not "features". Under XP, separation would gradually appear on its own if actual change patterns leaned that way.
- From YouArentGoingToNeedIt: "Even if you're totally, totally, totally sure that you'll need a feature later on, don't implement it now." (Emphasis mine.) Under XP, separation is implemented during ReFactoring, if not done from the start as good coding practice.
- That's an example, not a definition. And re-factoring is decided by need and OnceAndOnlyOnce.
- Each GUI system is so different in design and feel such that building a MirrorModel in between to make swapping easier is usually not worth it unless there is a known likely need to swap up front. And again, concerns interweave. Separating on one concern often forces de-separating on another. And you have not provided a clear definition of biz versus presentation anyhow such that "lessening the cognitive load" is not likely to take place. Vagueness increases cognitive load. And demonstrate that maintenance is simpler via code change walks. Count the finger and eye and mouse movements. Measure something. Show me something SPECIFIC is less. Show me, don't claim.
- Could you provide a coded example to show what you mean by a "MirrorModel in between"? I build large systems employing SeparationOfConcerns with business logic and presentation logic highly separated, and I don't create any MirrorModels, but maybe my interpretation of MirrorModel is different from yours. You don't mean an API, do you?
- [You don't need to build a MirrorModel. To create an application using one GUI system, you need to write some amount of code. If you write the presentational parts of the code separately from the business parts---which isn't nearly as complicated or vague as you make it out to be---that'll allow you to replace the presentational parts totally with those needed to use some other GUI system. To demonstrate this, here's an example from your other correspondent:]
r = a + b
writeln(r)
- [Suppose we want to change the above application to run on a Web page:]
r = a + b
document.write(r)
- [Notice that the business-logic portion of the code is completely unchanged and untouched, while the presentational part has been swapped to the new GUI. Thus the concept is demonstrated for the simplest-possible example; it applies similarly to less trivial applications. -DavidMcLean?]
- Just do this:
function writeln(x)
document.write(x)
end function
- [Yes. That's the same thing. -DavidMcLean?]
- But you said I would have to change the app code. I didn't have to.
- You appear to be levering the trivial nature of the example to make a point. How would your solution apply to a larger system, in which "writeln" and "document.write" here are merely representative of modules?
- It's all I have to go on, I cannot read your mind.
- You have not answered my question.
- I don't understand your question, especially the "representative of modules".
- How would your solution -- which appears to consist of overloading a function -- apply to a larger, modular system? If writeln(x) is representative of a presentation layer consisting of 25 classes and 250 methods, how does your approach work?
- If your design relies on a function or clause or statement style that cannot be re-worked or overridden, it's probably a bad design. Generally if there is a frequent "pathy" module reference, it's good practice to make a local short-cut in case the path changes in the future because long paths are fairly likely to change, and take up a lot of screen real-estate:
bldgp = big.long.dotty.giant.path; // (but find a better name than shown here)
...
bldgp.write(...);
// Instead of:
big.long.dotty.giant.path.write(...);
.
- That's not what I meant. The module path is irrelevant. You suggested that you can effectively deal with change presentation logic inside business logic, without changing the business logic, via function overriding. How does that work with modules and classes? Are you suggesting polymorphism? You're then coding to an abstract interface, and providing multiple concrete implementations somewhere else. If so, that's reasonable, but it implies that you've separated business logic from presentation logic.
- I don't know what you mean. What is "it", and where did I say anything about "inside"?
- Your scenario is unlikely anyhow. Desktop GUI's have so much more power and options than web GUI's that dropping existing code in as-is is quite unrealistic. A heavy rewrite would be in order regardless. A lot of the code may have to be converted into client-side JavaScript also such that we'd have to take it out of the original language.
- It was intended as a conceptual illustration, not actual code. SeparationOfConcerns facilitates said re-write.
- Bullshit.
- Why do you say that? It's obvious. If business logic is separate from presentation logic, you don't need to look at the business logic, let along change it, to replace the presentation logic layer.
- That's just a word play. The total code that needs changing would be the same. One can take two lines of code and put one in Zib and the other in Fot and say, "Hey, look, I separated Zib from Fot! Isn't that lovely?"
- What do you mean by "that's just a word play"? It's not the total amount of code that needs changing which is an issue, but where the code lies and what we need to change, how much effort it takes, and what we risk by making changes.
- Yes, but I was focusing on your particular example and your related claims about it.
- Sorry, I'm not clear how that addresses my question or my point.
- You claimed it "facilitates said re-write", but you have not identified a need for a rewrite yet.
- [You identified the need for a rewrite yourself, Top. Above, you said: "A heavy rewrite would be in order regardless." That's the rewrite to which your correspondent was referring. -DavidMcLean?]
- I meant your "writeln" example. If you claim on something bigger, than show the bigger code.
- [I didn't say that you'd have to change the app code but did imply it, so you've a point. The issue there is that the simplest-possible example is too simple: There's only a single function in the example that represents presentation logic, so it's effectively already separated into presentational and business logic. A minimally better demonstration might look more like this: -DavidMcLean?]
r = a + b
writeln(colouredRed(r))
// is changed for the Web version to:
r = a + b
document.write(cssRed(r))
- [Sure, you could define writeln() for the browser environment, and colouredRed(), and so on, but where do you stop? Doing it that way is in fact building a MirrorModel, which is a situation I gather you'd prefer to avoid. -DavidMcLean?]
- I'm not following neither the example or your statement. Please clarify. What is "goes to"?
- [I clarified the above. Please review and ask again if there's further confusion. -DavidMcLean?]
- Why would we have to change it into the second? I thought I demonstrated we wouldn't have to. (If a language doesn't let us override certain functions, that's a problem with the language itself, not a general design issue.)
- The single function is for the sake of illustration. Assume it represents a module.
- Sorry, I don't see how that changes anything. The function could call the longer form if need be. If anything, "pathy" module references make for more change needs (aa.bb.cc.dd.write(...)), but that's kind of a diff topic. Code up a mock "module" and we'll revisit this. I cannot read your mind.
- If you're arguing against modularity because of the cost of its scaffolding, then you're either not using the facilities of the language to simplify module references, or your applications are consistently too small for any of this to matter much or for you to recognise the needs that drive why we employ SeparationOfConcerns.
- See the last paragraph that starts "There is rarely any ideal partitioning..."
- That's a conclusion. Could you provide evidence for it? It runs contrary to established best practice, so the BurdenOfProof lies with you.
- ArgumentFromAuthority does NOT set the default position. And there is no "established best practice", just bullshit fads and anti-scientific evangelists.
- Your conclusion is contrary to the mainstream view, regardless of your opinion of it or how it came to be. Your claims -- no matter how reasonable you feel them to be and no matter how much you feel your opponents are "anti-scientific evangelists" -- are objectively exceptional. Therefore, the BurdenOfProof lies with you.
- "Objectively exceptional" my ass. Even if that claptrap was true, ArgumentFromPopularity does not set the default position. You are making up debate rules. ArgumentFromAuthority is for intellectual cowards. Come back when you have a concrete example instead of concrete between your ears.
- [It is objectively exceptional, because it objectively differs from the majority best-practice way of thinking; that doesn't mean it's necessarily bad or wrong, but it does mean that the BurdenOfProof lies with you, as you're the one presenting the exceptional position. You presume that setting a "default position" requires an argument, and therefore that arguing "the default should be X because that's what everyone accepts" is a fallacious ArgumentFromPopularity, but the default position isn't null. No argument must be made to declare a "default position" of "that which is commonly accepted", therefore no logical fallacy may arise. In order to shift the situation from "that which is commonly accepted" to your objectively exceptional alternative, you'll need to provide proof. -DavidMcLean?]
- First, you haven't taken/found a formal survey. You only claim it's popular/loved/in-style. Second, you haven't sufficiently defined the entire concept such that one cannot tell if they are "doing it" or not. Third, the default position is "unknown" until either side brings evidence. Popularity does not set the default position. Claiming it does will not make it so; it's wrong and repetition of that ridiculous claim is NOT going to change my mind, and I hope any readers reject that dark-age-like position also.
- [The default position isn't "unknown" until either side brings evidence. If you claim it is, shove it; it's wrong, and repetition of that ridiculous claim is not going to change my mind, and I hope any readers flush that dark-age garbage also. … seriously, though, the reason ShiftingTheBurdenOfProof exists is because the default position isn't unknown, it's that-which-is-commonly-accepted. To use a frivolous example, I might come along and loudly argue that Bigfoot is in fact real; it would be perfectly reasonable for others to ask me for proof of this fact, but it would be unreasonable and ShiftingTheBurdenOfProof if I promptly responded "no, you need to provide proof that he doesn't exist"---because the default state is that which is commonly accepted ("Bigfoot does not exist"), not simply "unknown". The default state being that-which-is-commonly-accepted does not constitute an argument that that-which-is-commonly-accepted is also true, however, therefore no ArgumentFromAuthority or ArgumentFromPopularity is invoked; it merely constitutes the current state of things, which one might try to change by presenting arguments toward some different state. Indeed, if you've a compelling argument supporting your thesis that SeparationOfConcerns is not of value, it would be a good idea to present it and perhaps convince your correspondents. Decrying SeparationOfConcerns and asking us for proof of its value, by contrast, is ShiftingTheBurdenOfProof because that-which-is-commonly-accepted is that it's of value. -DavidMcLean?]
- The default for big-foot is indeed "unknown" rather than "doesn't exist". See DefaultStanceIsUnknown.(I re-edited some of my language to make it a bit mellower. I was venting at the time out of frustration.)
- The default for big-foot is "we need proof that it exists", not "we need proof it doesn't exist". Similarly, the default for the validity of SeparationOfConcerns along business logic and presentation logic lines -- which, unless you've been ignoring SoftwareEngineering trends for over two decades, is considered industry "best practice" whether individual shops (or programmers) do it or not -- is "we need proof that it's invalid", not "we need proof that it's valid". The SoftwareEngineering community already agrees it's valid, and you'll need to provide a compelling argument against it for us (and the industry) to take any notice. I see no reason to bother arguing with you otherwise, because in the absence of a compelling argument against our view, you're naught but a lone dissenter aimlessly chucking rhetorical pebbles. You can easily be ignored. That doesn't mean you aren't right -- you might well be able to present an argument that turns the industry -- but in the absence of a compelling argument, and only a compelling argument from you, we'll never know because we'll soon ignore your requests to provide evidence for that which we prove to ourselves and our clients every day in our jobs.
- Your rule seems to be: "If the majority believe X is a valid process, then the burden is on those who claim it's invalid". I reject that as rational debate logic. NO PRACTICE gets a get-out-of-science-card via popularity votes. Take it to DefaultStanceIsUnknown if you want to provide a proof or rational for that rule in a general sense. Further, I believe most would agree to "leaning toward" separation of B&P, but also agree that ItDepends. Most in the field would agree to SOME level of ItDepends I'd be willing to bet, NOT your absolute stance. Thus, it is not an "exceptional viewpoint", as you claim. Without real surveys (and a real definition), that cannot be answered here and so there is no reason for re-re-re-repeating your view of that repeatedly in multiple places redundantly multiple times repeatedly.
- My rule isn't simply "[i]f the majority believe X is a valid process, then the burden is on those who claim it's invalid". The case here is that not only do the majority believe X is a valid process, we also experience working evidence for it on a daily basis. We experience its confirmation, continuously, as part of the development process. Therefore, the burden on you is to demonstrate a valid alternative to something that we know works well. Otherwise, why should we waste time proving something to you -- and no one else, as far as I can tell -- for which we have working evidence of its validity?
- I witness stuff also. Your anecdotes are not specially blessed.
- I'm speaking of developers in general, not my personal experience.
- As for ItDepends, no one is claiming there aren't exceptions to the guideline, but exceptions should be treated as exceptional, not the norm. We don't need to coat every guideline in a thick padding of disclaimers. Experts know how to deal with exceptions and don't need disclaimers, and beginners don't yet have the perspective or expertise to appreciate the exceptions. (See DreyfusModel) Therefore, a guideline stated as unconditional -- not stated as ItDepends, even though occasionally ItDepends -- is both appropriate and (in this particular case) recognised by experts as indubitably valid and good practice.
- Again, you have no survey. YagNi is also a recognized "best practice" (practiced to various degree based on probability estimates).
- Survey for what? Again, YagNi is about implementing features, not development practices.
- No survey about commonality to establish "exceptional". I'll leave the YagNi definition issue to the section further up.
- Given that every application development tool or framework over the last two decades has emphasised SeparationOfConcerns, either implicitly or explicitly (see, for example, http://www.asp.net/mvc/overview/getting-started -- "... enables a clean separation of concerns ..."), and it's been a de facto guiding principle of SoftwareEngineering for considerably longer (it's why we have "procedures" and "modules" and "namespaces" and "classes" and "structures"), no formal survey is needed. It is established practice. Therefore, if you're going to claim SeparationOfConcerns is invalid, the BurdenOfProof lies with you.
- Again again, I am NOT against SOC in general. I'm against SOC for "biz versus presentation" as an absolute rule, or at least believe it to be too poorly-defined to put any stamp of approval on. It's too simplistic to say "always do X" when there are competing division candidates (including having none). -t
- Almost every best practice can be countered with a (possibly contrived) counter-case or two. That does not invalidate the best practice, and the simplest way to describe it is "always do X" and rely on the expertise and judgement of the user to know when an exception precludes "X".
- Without specific realistic scenarios or actual code to mutually study, it's hard to say how often and why the "exceptions" occur. Until something more specific to dissect, we are just going in circles ArguingAboutArguing?.
If you split by presentation-ness, then the condition has to be duplicated in two places: the presentation side and the non-presentation side, making
two spots that would need changing under activation logic change requests instead of one. (Note: I use a VB-like syntax because it's easier to recover from
TabMunging, not because I'm promoting a syntax style.)
Maybe if I saw some sample semi-realistic code and we went though typical changes together we can see such directly and compare and contrast alternatives. It may be what you feel is "common" differs from my experience. That happens, for each brain tends to remember different kinds of events over time.
There is rarely any ideal partitioning factor/dimension/aspect. Most apps involve a good many cross-cutting aspects and grouping on Factor A de-groups on Factor B, hurting Aspect B change costs. And it also may vary per app or per domain such that there is no universal "best" partitioning, only least-evil compromises. Any general or "always partition by X" rule is highly suspect. SimulationOfTheFuture by comparing each candidate grouping is my favorite technique for choosing division aspects for reasons given in the topic.
See BusinessLogicDefinition
MayThirteen