The past three posts of this little mini-series
   have gone from a    >Working definition of business logic
   to a    >Rigorous definition of business logic
   and on to some    >theorems about business logic.
   To wrap things up, I'd like to ask the question,
   is it possible to isolate business logic into
   a single tier?  
Related Reading
There are plenty of opinions out there.
   For a pretty thorough explanation of how to put
   everything into the DBMS, check out 
      >Toon Koppelaar's description.  Mr.
   Koppelaars has some good material, but you do 
   need to read through his earlier posts to get
   the definitions of some of his terms.  You can also
   follow his links through to some high quality
   discussions elsewhere.
Contrasting Mr. Koppelaar's opinion is a piece
   which does not have nearly the same impact, IMHO,
   because in 
      >Dude, Where's My Business Logic? we get some solid
   history mixed with normative assertions based on
   either anecdote or nothing at all.  I'm a big believer
   in anecdote, but when I read a sentence that 
   says, "The database should not have any knowledge of what a customer is, but only of the elements that are used to store a customer." then
   I figure I'm dealing with somebody who needs to see 
   a bit more of the world.
Starting At the Top: The User Interface
First, let's review that our rigorous definition of business logic 
   includes schema (types and constraints), 
   derived values (timestamps, userstamps, calculations,
   histories), non-algorithmic compound operations
   (like batch billing) and algorithmic compound
   operations, those that require looping in their
   code.  This encompasses everything we might do
   from the simplest passive things like a constraint
   that prevents discounts from being over 100% to
   the most complex hours-long business process,
   along with everything in between accounted for.
Now I want to start out by using that definition
   to see a little bit about what is going on in
   the User Interface.  This is not the presentation
   layer as it is often called but the interaction
   layer and even the command layer.
Consider an admin interface to 
   a database, where the user is entering or modifying
   prices for the price list.  Now, if the user could
   enter "Kim Stanley Robinson" as the price, that would be
   kind of silly, so of course the numeric inputs
   only allow numeric values.  Same goes for dates. 
So the foundation of usability for a UI 
   is at very least
   knowlege of and enforcement of types in
   the UI layer.  Don't be scared off that I am
   claiming the UI is enforcing anything, we'll 
   get to that a little lower down.
Now consider the case where the user is 
   typing in a discount rate for this or that,
   and a discount is not allowed to be over 100%.
   The UI really ought to enforce this,
   otherwise the user's time is wasted when she
   enters an invalid value, finishes the entire form,
   and only then gets an error when she tries to
   save.  In the database world we call this
   a constraint, so the UI needs to know about
   constraints to better serve the user.
Now this same user is typing a form where there
   is an entry for US State.  The allowed values are
   in a table in the database, and it would be nice
   if the user had a drop-down list, and one that
   was auto-suggesting as the user typed.  Of course 
   the easiest way to do something like this is just
   make sure the UI form "knows" that this field is
   a foreign key to the STATES table, so it can generate
   the list using some generic library function that
   grabs a couple of columns out of the STATES
   table.  Of course, this kind of lookup thing will
   be happening all over the place, so it would work
   well if the UI knew about and enforced foreign
   keys during entry.
And I suppose the user might at some point be 
   entering a purchase order.  The purchase order is
   automatically stamped with today's date.  The
   user might see it, but not be able to change it,
   so now our UI knows about system-generated values.
Is this user allowed to delete a customer?
   If not, the button should either be grayed out or not
   be there at all.  The UI needs to know about 
   and enforce some security.
More About Knowing and Enforcing
So in fact the UI layer not only knows the logic
   but is enforcing it.  It is enforcing it for
   two reasons, to improve the user experience with
   date pickers, lists, and so forth, and to prevent the user
   from entering invalid data and wasting round trips.
And yet, because we cannot trust what comes in
   to the web server over the wire, we have to
   enforce every single rule a second time when
   we commit the data.
You usually do not hear people say that the UI
   enforces business logic.  They usually say the
   opposite.  But the UI does enforce business logic.
   The problem is, everything the UI enforces has
   to be enforced again.  That may be why we often
   overlook the fact that it is doing so.
The Application and The Database
Now let's go through the stuff the UI is
   enforcing, and see
   what happens in the application and the database.
With respect to type, a strongly typed language
   will throw an error if the type is wrong, and a weakly
   typed language is wise to put in a type check anyway.
   The the DBMS is going to only allow correctly typed
   values, so, including the UI, 
   type is enforced three times.
With respect to lookups like US state, in
   a SQL database we always let the server do that
   with a foreign key, if we know what is good for
   us.  That makes double enforcement for lookups.
So we can see where this is going.  As we look at
   constraints and security and anything else that
   must be right, we find it will be enforced at least
   twice, and as much as three times.
You Cannot Isolate What Must be Duplicated
By defining First Order Business Logic, the simplest
   foundation layer, as including things like types
   and keys and constraints, we find that the enforcement
   of this First Order stuff is done 2 or 3 times, but
   never only once.  
This more or less leaves in tatters the idea of a 
   "Business Logic Layer" that is in any way capable of
   handling all business logic all by its lonesome.
   The UI layer is completely useless unless it is
   also enforcing as much logic as possible, and 
   even when we leave the Database Server as the
   final enforcer of First Order Business Logic
   (types, constraints, keys), it is still often good
   engineering to do some checks to prevent 
   expensive wasted trips to the server.
So we are wasting time if we sit around trying to figure
   out how to get the Business Logic 
   "where it belongs", because it "belongs" in at
   least two places and sometimes three.  Herding 
   the cats into a single pen is a fool's errand, it
   is at once unnecessary, undesirable, and impossible.
Update: Regular reader Dean Thrasher of Infovark summarizes
   most of what I'm saying here using an apt industry
   standard term: Business Logic is a cross-cutting concern.
Some Real Questions
Only when we have smashed the concept that Business
   Logic can exist in serene isolation in its own layer
   can we start to ask the questions that would actually
   speed up development and make for better engineering.
Freed of the illusion of a separate layer, when we
   look at the higher Third and Fourth Order Business
   Logic, which always require coding, we can decide where
   they go based either on    >engineering or the availability of qualified
   programmers in particular technologies,
   but we should not make
   the mistake of believing they are going where they
   go because the gods would have it so.
But the real pressing question if we are seeking
   to create efficient manageable large systems is
   this: how we distribute
   the same business logic into 2 or 3 (or more)
   different places so that it is enforced
   consistently everywhere.  Because a smaller code
   base is always easier to manage than a large one,
   and because configuration is always easier than
   coding, this comes down to meta-data, or if you
   prefer, a data dictionary.  That's the trick that
   always worked for me.
Is This Only A Matter of Definitions?
Anybody who disagrees with the thesis here has
   only to say, "Ken, those things are not business
   logic just because you wrote a blog that says they
   are.  In my world business logic is about code
   baby!"  Well sure, have it your way.
   After all, the nice thing about definitions is that we
   can all pick the ones we like. 
But these definitions, the theorems I derived on
   Tuesday, and the multiple-enforcement thesis presented
   here today should make sense to anbyody struggling
   with where to put the business logic.  That struggle
   and its frustrations come from the mistake of
   imposing abstract
   conceptual responsibilities on each tier instead
   of using the tiers as each is able to get the
   job done.  Databases are wonderful for type,
   entity integrity (uniqueness), referential integrity,
   ACID compliance, and many other things.  Use them!
   Code is often better when the problem at hand cannot
   be solved with a combination of keys and constraints
   (Fourth Order Business Logic), but even that code can
   be put into the DB or in the application.
So beware of paradigms that assign responsibility
   without compromise to this or that tier.  It cannot
   be done.  Don't be afraid to use code for doing things
   that require structured imperative step-wise operations,
   and don't be afraid to use the database for what it is
   good for, and leave the arguments about "where everything
   belongs" to those with too much time on their hands.
No comments:
Post a Comment