There is a school of thought, prevalent today, that says: A Domain model should always be independent of (and thus never call) the Persistence- (aka Repository) or Service- layers. I dont yet know of an established name for the architecture implied by the rule, so I shall refer to it as a Independent Domain and the reverse, architectures where the domain model references Repositories (or the like) a Co-dependent Domain.
I dont subscribe to the view that Co-dependent Domains are bad or prohibited. While Independent Domains work well in many situations, there are times where its practical, or just plain tractable, to back a domain-object method with a query to a repository. Other times, we would like a domain object to emit an domain event to a subscriber in the service layer, who might convert it into an email. In fact, rigid adherence to an Independent Domain rule can cause harm by encouraging domains to become Anemic.
- Rich Domains with classic object-oriented encapsulation are desirable. In such a system, the service layer is thin and virtually all code lives on the domain objects.
- An architecture loosely like diagram (dotted arrows denote “depends upon” or “knows about”).
- In an Independent Domain, the red arrow is not present, and thus generally the blue arrows may aquire extra reponsiblities to compensate.
- Repositories (loosely as described by Evans or Fowler) are the main interfaces through which the upper part of the system interacts with the ORM layer.
- We’re mainly talking about data reads here, ie database queries.
[Notice that there are a lot of one way relationships between adjacent tiers; we developers have learned that the mssing depedencies are extraneous. But nonetheless, the reason for every missing (prohibited) relationship needs to be understood and justified. Its is not sufficient to reflexsively ban them to make the diagram “pretty” or “symmetric”.]
- If domain objects depend on Repositories, they cannot be used in tiers/contexts where there is no open session to the persistence layer.
- Domain object depedency on persistence makes unit testing them more difficult
- In Jimmy Nilsson’s Applying Domain Driven Design, an overview diagram on page 116 depicts a Independent Domain, though he doesnt say why.
- In Fowler’s enterpise patterns book, the example code for Data Mapper uses a Co-Dependent Domain (ie Albumn Finder).
- In Javas J2EE 5 model, Entity Beans represent the Domain Model, Session Beans the service layer, and EntityManagers provide a gateway to the ORM layer. Sessions Beans can have the Entity Manager injected, but Entity Beans cannot, so clearly EJB 3 advocates the Independent Domain principle. Part of the reason, I think, is that J2EE promotes an Anemic Domain as a core principle, so they like to put business logic into their service layer, and make domain objects Dumb and Passive. Eg JBoss’s Bill Burke inEnterprise Java Beans 3.0: “Session Beans … are the appropriate place for business logic”. Its not my preferred architecture, but I’ll explore and evaluate the reasons they did that in another post.
- Bill Evans Domain Driven Design book supports a Co-Dependent domain, though he’s not entiely explicit and it required some careful reading to discern his positon.
- Some of my workmates favor an Independent Domain. I think the motivator is the appealing idea that a domain model should be able to exist in isolation. Certainly appealing, but is it workable in all cases?
Thats all Im aware of now. So Im left unsure if there are other any reasons. If anyone knows of more, good arguments for prescribing an Independent Domain, please send them to me.
I have yet to read any explicit argument outlining where Co-Dependent Domains are best – hence the need for this article.
When should Domain Objects access Repositories?
When they need to get references to other objects they interact with, and main alternative, traversal of the domain model relationships, doesn’t work well.
In an Independent Domain, where entities cannot query repositories, how will they get references to other objects?
- They have OR mapped relationships, set by the ORM layer when it loads the entity. If they need an object they dont have a reference to, they must traverse a path through intermediate relationships. This is the normal way that most entities communicate. Now, recall that object relations are unidirectional, in contrast to databases where with appropriate indexes, relations can be treated as being bidirectional. So there is the likelihood that our system’s domain model, even though fully connected, will not allow us to traverse everywhere within it, unless we deliberately create bidrectional relationships in the domain.
- They get passed them in method calls as parameters from the Service Layer. This just adds extra work for the calling code and leads to bloated method parameters.
Limits Of Domain Object Traversal
There are things you cannot do efficiently by traversing domain object pointers:
- Traversing a relationship with a high multiplicty, and then narrowing down the related domain object by some criteria. By High Multiplicity Relationships, I mean a many-to-one relationship between two entities, where the many side is numerous. It becomes impractical to traverse from the one side to the many side in the domain layer. Typically, we’re not interested in whole collection, just a small subset of them, so we dont want to have to load them into memory just to search through.
- Finding sums and similar aggregate functions of many small object. This is much more efficiently accomplished directly in the database.
Loose Coupling and Interface-Based Design
One of the reasons people advocate Indenpendent Domains is that they fear that referencing persistence- or service- layer classes will couple their domain to its application environment. It seems to me this confuses interface and implementation.
Your domain should access other layers through interfaces that are meaningful within the domain. When accessing the persistence layer, use a Repository interface that models the concept of “the collection all domain objects of type X in the world”, that could be backed a hashtable for all the domain knows. Similarly, when pushing events to the service layer, use a listener interface that does not reveal that the listener is outside the domain.
Linking the Domain to its Surrounding Layers
A co-dependent domain can be tricky to link to surrounding layers. Domain objects are usually created by the persistence framework (eg Hibernate), so injecting services into them before first use by the app requires interception of the create or load event.
Spring’s recomended approach require use of AspectJ class weaving to intercept the calling of domain objects constructors.
Most persistence frameworks should provide an on-load eent that can be hooked into (Hibernate does).
An alternative is to use a globally (ie statically) accessible Service Locator that the domain can use to lookup dependencies. It will work, but dependency injection is preferable.