This post is part of a series I am writing on Domain Driven Design (DDD).
"An Entity is an Object that represents something with continuity and identity. An entity is tracked through different states and implementations."
This is the definition of an entity according to DDD. So roughly, an entity is any object that is persisted in the database. Any class that is not stored in DB is not an entity.
IMHO an entity is the single most important constituent of DDD, simply because a vast majority of business logic would be owned by the entity. I use the term 'own' broadly to encompass all logic that is triggered by an entity but not necessarily present in the entity.
Creating an Entity
A good entity has to be both, lean and powerful. A normal entity typically has a number of attributes. To make this entity lean and powerful,
- Strip the entity to contain only its identifier attributes.
- Then add methods which are core to the concept/idea represented by that entity.
- Now add only attributes needed by these core set of methods.
This is all that there is to an entity.
Sample Scenario for discussion
The following example will be used for discussion.
A Container contains multiple boxes, each of which contain the same product. A CustomerOrder contains multiple boxes, one of each kind of product.
Customer order (1) ---------> (n) Box
Container (1) ---------> (n) Box
So a customer order indirectly refers to (n) Containers where n is the number of distinct products in the order.
Users are allowed to add/remove a new box to/from the order. Duplicate boxes for same customer order are merged.
Fine-tuning the Entity
To fine-tune the raw entity we have, we add in some more rules. These rules were based upon commonly encountered issues we faced. The main issues/rules are summarized below
1) Exposing Getters/Setters
Exposing attributes publicly, leads to a loss of encapsulation. No other class should be allowed to change the state of an entity, other than that entity itself. Other entities/classes can only trigger a business method and during execution of that business method,the entity itself mutates its state.
Bottom line: Never expose getters/setters in the entities.
Tip: If you use hibernate use field access to avoid setters.
2) Can Entities be exposed to application services ?
Application Layer sits above the domain layer. UI talks to this layer which orchestrates the call with classes in the domain layer. The straightforward answer is yes, the entities have to be exposed to application services so that they can be called when appropriate events are triggered in UI.
However the tricky part is whether the application services has access to all methods in the entity. In my experience, every entity has a bunch of granular methods that are exposed to other entities so that a complex workflow can be built from them. In the example above, removing a box from a container is a method exposed on the container. Typically this will be called by Order when a product is removed from it. But is Container.removeBox a method that needs to be exposed to application tier ? No.
Order needs access to this method but OrderApplicationService does not. When using java, a couple of alternatives possible are
Make method as package private provided Order and Container are in same package.
Have an interface over Container and let OrderApplicationService use only methods specified in the interface.
I prefer using alternative 2, since then, this method can be used throughout my domain layer but it wont be accessible outside of it. This also alleviates the needs to dump all entities into same package.
Bottom line: Entities have to be exposed but not all of its methods have to be.
Tip: Expose only relevant methods from entity to Application service layer
3) If an Entity's core method needs access to data in Db, can it hold a reference to DAOs ?
Yes. There is no harm in an entity using DAO's to look up data from DB. If a subset of some large set of data is to be updated, looking up data to modify is more performant than loading the entire set using the ORMs hydration and then iterating through the entire set.
This question has been discussed in great detail in the DDD forums and can be found here, here and here.
Tip: If using Spring 2.x use @Configurable notation to inject DAO reference into entities (or) if using Hibernate, inject the dependency using the onLoad method in Interceptor. More ways of injecting into Entities is detailed here.
4) Can an entity use domain services ?
Domain services contain methods that do not logically belong in one entity. The only difference between these services and application services is that domain services have access to all domain objects and all operations on them. In contrast application services can see only operations exposed by interface they use.
As with DAOs there is no harm in an entity using domain services. When looking at aggregates we will look at are some interesting scenarios where domain services are used.
5) Few entities need to access properties on another entity, during some of its operations. How to control this.
See 1, 2 above. No direct property access is to be allowed. Expose business methods that can mutate state, which, other entities can call.
There are 2 variants of which entities need access to which method
- Entities belonging to same aggregate - Typically these are all part of same package, so expose methods as package private methods.
- Entities from different aggregates - Expose the methods as public but ensure these methods are not present in Interface exposed to application layer
6) What kind of Entity methods should be exposed to Application layer?
Application layer typically should not act as a low-level orchestrator. It should not be responsible for calling a sequence of fine grained methods in correct sequence. It should only call a few coarse grained methods. These coarse grained methods should hide the complex work flow from the application layer.
Bottom line: Expose only coarse grained methods.
A typical rule of thumb that we used successfully to define coarse grained is - "A method is coarse grained, if it returns leaving the domain objects in a consistent and persistable state". Meaning all entities involved should be left in a valid state i.e. all mandatory params are set, all relationships are valid. The entities involved can be persisted as-is without any changes.
For e.g. in our example above,Container.removeBox is not a coarse grained method since it leaves the removed box without any valid owner. However methods like Container.moveBoxTo(Container newOwner) is valid since all entities are left with valid relationships.
Technorati Tags: DDD, Domain Driven Design