Help

Kinda tangentially related to this discussion, I'm often asked whether I believe in rich domain models or anemic domain models. I guess I'm pretty much ignorant as to what these terms really mean, since I've never seen a proper definition of either term.

If by anemic domain model, we mean /never/ writing any business logic on our object model, and if by rich domain model, we mean /writing as much business logic as possible/ on our domain model, then I certainly don't believe in either approach. But these seem to be caricatures; I don't think anyone follows either extreme.

So how do /I/ decide if method that implements business logic might belong on the domain model?

Well, here I'm guided by an understanding that the domain model (entity classes) are /the most reusable/ classes in my codebase. So I don't want to put any logic there that has dependencies to any state or collaborating classes /other than/:

  • state held in the domain model (the persistent attributes), and
  • other domain model classes.

In particular, I would never write code that calls out to external services, or accesses the database, or calls an EJB/Seam/Spring component in my entity class. I want my domain model to be /completely self-contained!/

So anytime you find yourself wishing that entities supported injection, or find yourself writing a JNDI lookup in a method of an entity, please consider that your domain model is no longer self-contained, and will be less reusable in different execution environments.

I'm not saying it's /wrong/ to disobey these rules. There's no right or wrong in design. But I think there's good value having a self-contained domain model, and I've never seen a situation where this idea was impractical.

15 comments:
 
09. Nov 2007, 09:02 CET | Link
Gerson Motoyama | gersonk(AT)gmail.com

Gavin

What about when an entity accesses a repository (DDD)? It would be nice if seam could make some sort of injecting repositories into an entity... It'll help a lot (for DDD users)!

See Spring's @Configurable annotation.

ReplyQuote
 
09. Nov 2007, 11:30 CET | Link

Repository is just a fancy new word someone made up for DAO. So no, don't call DAOs in your domain model implementation classes.

 
09. Nov 2007, 12:48 CET | Link

Past year, TheServerSide posted a propossal I wrote to reduce DAO responsabilities in JPA. My idea is named ImplicitQuery. I have a detailed description here.

 
09. Nov 2007, 14:13 CET | Link

Currently I am reading Pojo's in action. This books says the same thing. Stuff like sending an E-Mail should be within a Service class and not the Entity. But it does say that it is allowed to use Repositories (DAO's) within the Entity Bean. Only the difference is that the Repository should be given to the method via a method parameter and not be injected. That way the entity can be instantiated without being directly dependend on the repository. I know that this book isn't holy, but what do you think about this approach.

 
09. Nov 2007, 14:18 CET | Link
Sakuraba

True dat!

The only logic I allow on entities are maybe some custom /addToCollectionRelationship(List newEntires)/ to be in charge of validating entries of the parameter List against the entry that this method is called on.

Calling DAOs from inside an entity is like calling a PL/SQL function from inside a database table row.

 
09. Nov 2007, 14:34 CET | Link

I personally would not call a DAO from an entity class. And I've never really run into a circumstance where that felt natural.

 
09. Nov 2007, 14:35 CET | Link
The only logic I allow on entities are maybe some custom addToCollectionRelationship(List newEntires) to be in charge of validating entries of the parameter List against the entry that this method is called on.

To be clear, I certainly write more interesting methods than just association management on the domain model. For example, Order.getTotal(), which calculates the total of all LineItems is perfectly reasonable.

 
09. Nov 2007, 14:51 CET | Link

Well, the proposal I quoted before, allows you to bind a named query result to an Entity property:

@NamedQuery( name Salesman.calculateSalary, query SELECT sum(s.sales) FROM Salesman s WHERE s eq :this )

class Salesman{

@NamedQuery(Salesman.calculateSalary) public Long getSalary(){...}

}

Query result is binded when Entity is readed from DB.

09. Nov 2007, 19:17 CET | Link
Chris Bredesen | cbredesen(AT)redhat.com

One positive side effect of encapsulation of /sensible/ behavior as described by Gavin is the wonderful unit testability you gain. The domain model can (and should) be quickly and effectively tested in complete isolation. Of course, we have the tools today to test other bits without a container, but when you can test a good portion of your domain's behavior in milliseconds instead of seconds (or minutes), test-driven design becomes much more attractive.

 
09. Nov 2007, 19:35 CET | Link
Sakuraba
For example, Order.getTotal(), which calculates the total of all LineItems is perfectly reasonable.

Good example. I think you can bring a lot of sophisticated stuff in there as long as it does not interact with application services.Entities shall be used by services, but not use services themself.

If we had better property access in the Java language, we could get rid of a lot of those gigantic getter/setter only classes.

Personally I dont like to a association management in a getter/setter pairs and another method to add + validate to a specific association. But if you annote your getters/setters, there is no real alternative. Or is there?

 
09. Nov 2007, 21:45 CET | Link

Couldn't agree with you more. I have experienced both ends of the spectrum at different jobs and neither one worked. Maintenance was awful since the entire model was so intertwined with the services.

I think the original data access tools that we used in Java resulted in a lot of bad development habits - of course I'm not counting Hibernate in this group. Because the tools were so limiting and so poorly performing, we had to use a lot of anti-patterns to build our applications. Since then the tools have gotten better and we're starting to see that yes....we can create lightweight isolated data models that contain business logic.

 
10. Nov 2007, 19:46 CET | Link
Gerson K. Motoyama | gersonk(AT)gmail.com

Christian,

Thanks for the reply.

Actually, a repository (DDD) is not exactly a DAO. A repository is a domain concept (hight level, domain centric) that allows the client to find aggregate root objects and reconstitute them from the underlying datastore. For the relational database, this can be done by implementing a DAO/Data Mapper (low level), but it's just a implementation detail that is not visible to the client of domain objects. In this way, it's just natural for the domain object accesses a repository of some domain type/aggregate.

I suggest you read a bit more about DDD and understand the main concepts behind it before making such assumption/conclusion.

Some links about the differences between DAO and Repository:

 
13. Nov 2007, 22:39 CET | Link
Steve

Gavin, what do you think about this text from the Acegi-security reference guide? I bet you love it. :D

Most people are interested in securing method invocations on their services layer. This is because the services layer is where most business logic resides in current-generation J2EE applications (for clarification, the author disapproves of this design and instead advocates properly encapsulated domain objects together with the DTO, assembly, facade and transparent persistence patterns, but as anemic domain objects is the present mainstream approach, we'll talk about it here)
 
29. Jan 2008, 16:31 CET | Link

Gavin,

so what would you do in case, if any change to an entity has direct influence on the database. Would that mean to have either one method in the entity and furthermore an method in a controller propagating this to the database? For example I think of removing/adding values to a Collection. This would mean either to remove this object from the collection, but also a call to the remove() of the EntityManager is necessary.

What would you say: is this a need to call a controller or should that also be done by the entity?

Thomas

Post Comment