By Peter Bell

Understanding the Anaemic Domain (responding to Jared - part 1)

Jared has just posted a thoughtful and thought provoking article on anaemic domain models and architectural approaches in CF. Unfortunately, I would argue that he has missed the boat in some ways and taken it to an extreme in others :-> (read the post, you'll get the reference)

The main point to note is that if you check out Martins Bliki, you’ll see that persisting of data is not the kind of rich object behavior that he is describing at all – it is business rules, validations, calculations and the like. He clearly states that putting data persistence code in business objects is orthogonal to his arguments on anaemia.

The truth is that many web application appear to be anaemic because they are primarily CRUD and the domain objects don’t HAVE rich behavior to model. If you don’t have validations, calculated properties or other similar business rules, your domain objects are going to look anaemic because there isn’t any behavior for them to contain!

The one comment I would make is that if you DO have rich getter behaviors (simple example – pull date of birth from db but display the users calculated Age on the screen), it is important to use them consistently on both single records and collections of records, so if you’re using ColdFusion where creation of objects is relatively expensive, you’re going to want to use something like my Iterating Business Object instead of a Gateway returning a recordset to your views for displaying collections of objects. Otherwise you are indeed misusing the service layer (from an OO best practices perspective).

I would argue that for most web apps, the test of a good design would not be how anaemic the business objects were (as that may be an artifact of the simplicity of your application). A better test would be how bloated your service layer is. The service layer can do many things, but it certainly shouldn’t be taking the place of getters and setters with rich behaviors in your domain objects.

All the usual provisos apply. Sometimes an anaemic design is more appropriate for a use case, sometimes OO is a waste of time, and sometimes assembler is the only language worth writing stuff in. This works for most of my universe of use cases. Your mileage may vary.

Comments
Peter,

Glad you noticed... and yeah, I get the reference. ;)

Martin's comments aside, my thoughts on adding persistence layer access to a bean came from the JavaBean spec, which indicates that a bean must be able to save its state in "cold storage" and retrieve it later. Hence, it indicates that persistence, according to the Java spec for beans, needs to be available... again, according to the Sun spec for JavaBeans. It's an interesting conflict that I see arising between ORM and POCFO (that's Plain Old ColdFusion Objects): to make your business objects smart enough to ask a composited "helper" to persist their state or to delegate that to some other entity from outside the model components.

I haven't seen anyone go so far as to put a getBirthdate() call on a Service-layer component... that would (IMO) fairly obviously go on the model component. I would call that "silly", not anemic. Since the term "anemia" comes to us from the medical condition, wherein the number of healthy blood cells is decreased, the parallel to healthy components, ones that have the capabilities they should, is pretty apparent. Having your service layer get an age from a birthdate instead of having a getter return the results of a dateAdd() call is actually rather laughable.

Obviously I have a lot more reading and thinking to do... and I'm sure I'll talk myself in and out of various approaches, but Martin's own Bliki covers TableGateway, RecordGateway, ORM, and DataMapper patterns to solve the problem of persistence in beans. I think it's safe to say there's more than one way to handle this, and, IF you use composition and a persistence delegate of some sort, you're still not adding persistence-layer-specific code to your beans.
# Posted By Jared Rypka-Hauer | 12/29/06 12:36 AM
Hi Jared,

Interesting - I hadn't seen the bean spec, so I am a little surprised that it defined persistence as one of the responsibilities of the bean as that definitely isn't a common approach in the Java world.

I should clarify my Age/DoB comment. Lets say you want to display a users age, you're clearly going to call User.getAge() - right? So, given that the vast majority of CF developers seem to use gateways, how do you call User.getAge() when you're cf outputting a recordset containing a collection of users? The only solution is to put the calculation in your view (yuck) or loop through the query in the service layer and create a new column called query.Age based on the query.DoB column (double yuck). My point is that unless you ask a db view to do the work (I'm not a big fan of putting business rules in the db although I get there are some use cases where it makes sense), you're either putting your domain logic (calculating age from DoB) into either your service method or your view. I agree that doing so could be considered laughable, but given that seems to be the default best practice in the CF world, I'm not laughing :->

I personally have a save method in my beans which uses composition to call the associated DAO which uses a DataMapper pattern to map the object into the appropriate inserts into the associated table (remember - your AdminUser may be stored across three normalized tables - so for non trivial ORM mappings there is a bunch of work to do within the data mapper).

For me the Table and Record Gateways don't make much sense. If you have objects where the objects correspond 1:1 to tables in the DB and the properties correspond 1:1 to columns in the table it works. But if your domain model is THAT simple, you probably don't need to be messing with an OO approach to solve your problem anyway.

Looking forward to learning more as you blog about your thinking as you go through this process!
# Posted By Peter Bell | 12/29/06 12:57 AM
Interestingly, it was thinking about this stuff a few months ago that drove LightWire. It has been pretty clearly stated that ColdSpring is not optimized for injecting dependencies into transients (not that it is impossible, but that it is now how it was intended to be used). But look at your composition. There is no reason why your persistence classes (lets call them DAOs for the sake of argument) should be transient, so how does your transient domain object speak to your singleton DAO? I want to be able to use either constructor or setter injection on my transients as I do on my singletons and that was why I started putting together LightWire. Still a lot of issues to think through about the best way to support custom object factories or to allow enough variability in LightWire to remove the need for them, and a bunch of Spring 2.0 features like session and conversation scopes as well as singleton and transient, but maybe now it makes more sense the problem I had which drove me to consider a lightweight DI engine optimized for injecting singletons into transients as well as into other singletons?
# Posted By Peter Bell | 12/29/06 1:02 AM
RE your "age" example. If the application wants to display the users age, I don't see anything wrong with putting this "calculation" in the "view" processing. After all, it's only another representaion of an atomic piece of data. Otherwise we're heading for some kind of abstraction Nirvana
# Posted By Richard Tugwell | 12/29/06 1:20 PM
Hi Richard,

Obviously if we are just dealing with a single property (user.Age) this isn't an issue at all. Now scale up and imagine a Product has a complex set of every changing business rules for displaying the correct Product.getPrice(). There are also complex rules for whether to display a value or not for Product.displayDiscount(). Add in User.getFullName() which concatenates first and last names and maybe 50-60 more custom getters and then you end up putting an awful lot of business logic into your SQL views.

Generally putting the code into SQL provides for better performance, but the tools for managing, commenting and version control tracking on SQL are generally less developed so it is often better to put that into your application. This is where OO really shines - handling lots of these complex business rules and making it easy to maintain them over time.
# Posted By Peter Bell | 12/29/06 1:26 PM
Hi Peter

Didn't mean SQL views - meant rendering logic. Probably talking at cross purposes
# Posted By Richard Tugwell | 12/29/06 1:56 PM
Maybe I should clarify my meaning

My application dispays a users age - model/gateway whatever returns users DOB, no need in my view for getAge(), and rendering logic calculates/displays this as AGE. Client now doesn't like this - wants to display "teeny", "cool guy", or "past it" depending on users age. I'd just redo this in the "view" as in MVC logic. No need to touch any of the business logic DB stuff. Easy to implement alternatives.
# Posted By Richard Tugwell | 12/29/06 2:11 PM
Ah, OK, but I'll still make the same point. Lets say you have a product list, product search, product view and cart screen - all have to display Product.Price. I would want to put that logic in one place in the Product business object - not in4 places (or a UDF or custom tag called by all 4) in the views.

As business logic gets more complex OO solves a bunch of those kinds of issues.
# Posted By Peter Bell | 12/29/06 2:12 PM
Works great as long as each business object is only ever related to a single view. As you get more and more views relating to the same business object (see example above but it can get much worse than this real world/complex systems) you would have to replicate your code all over the place. That was actually one of the real world pain points that moved me to start using OO for certain applications.
# Posted By Peter Bell | 12/29/06 2:14 PM
By the same token, you will have to implement methods for getAgeAsRealAge(), getAgeAs NonPCTerm() etc etc etc. I'd rather keep the getDOB() in the object and implement all these alternatives as UDF's.
# Posted By Richard Tugwell | 12/29/06 2:22 PM
Only if every view has a different display rule. In my experience if an object has 5 associated views, at least 3-4 will need the same business logic. UDF's also work for encapsulating this, but having tried both ways I've found life gets easier using OO as complexity increases. Still, to each his own!
# Posted By Peter Bell | 12/29/06 2:41 PM
"To each his own.." I concur! There are no right ways of doing things
# Posted By Richard Tugwell | 12/29/06 3:22 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.005.