Decoupled Domain Model

Applications which solve complex domain problems do not exist in vacuum. They have to interact with external systems and/or have some form of user interface. In our starting architecture domain model is used in presentation layer as well. This approach works well if the domain problem it is trying to solve is simple. But as the complexity rises these trade offs start to hurt.

  1. Persistence and serialization mismatch With modern object relational mapping tools, e.g. Hibernate, the domain model is persisted to the database. It is sensible from performance perspective to not load the entire graph of domain object from the database, when only part of it is used. Aspect oriented programming is employed for on demand retrieval of domain objects aka lazy loading. When presentation and application layers are in different runtime context the domain model also needs to be serialized. Domain entities which have few descendants the performance impact is negligible. Hence, the mixing of persistence concern slapped on domain objects via aspects and serialization mechanism can co-exist. But in most cases the performance problems are just waiting to happen. This can be caused by deep object graphs and reference objects being pointed to by transaction objects.
  2. Presentation concerns seeping into domain objects When the domain objects are used in the presentation layer they can start implementing presentation logic, as it is quite convenient. Although the augmenting view model provides a mechanism to avoid it but depends on the knowledge of the developer.
  3. Command processing can be confusing While request processing works quite well if augmenting view model is used correctly, command processing is not that simple if the domain objects are constructed to model the request for application layer. In the service layers domain objects are retrieved from the repository. So potentially one can have two domain objects representing the same entity. An update of an entity by the user is a canonical example of this. This is confusing because there are multiple ways of doing this. One can persist the request entity instance by attaching other transactional and reference domain objects to it. Another way would be to activate the persisted entity and update all the values onto this instance from the request instance. In the worst case in a given command two object might get used differently.
  4. Construction of entities ...

Decoupled domain model separates the domain model from the application layer's contract with its clients. A different set of objects called application contract objects are used by the application layer to interact with its client. This decoupling ensures that domain model is responsible only for domain logic and not responsible for presentation logic. The contracts are defined such that they can not carry anything more that required by the client. By doing this application layer clearly states its expectations in request and promise in the response.

There is an explicit mapping during query processing, from the domain objects to the response contract. This brings up a question about who should do the mapping, assemblers or domain object themselves. There are pros and cons on both choices. If domain objects construct or populate the application contract objects, one doesn't need to define any getters, preserving encapsulation. But on the other hand a single domain object might be used for different requests. This would result in domain object having multiple methods in them which do such work. As the primary responsibility of domain object is to implement business logic, this can become significant distraction, if there are a lot of such requests. When using hand coded assemblers these issues get reversed. There is another approach which is ride on object to object mapping frameworks like dozer. Since they use aspects they try to bring in the best of both worlds. ---experience report------

As only the data which needs to be mapped to the contract are used, serialization doesn't end up loading more entities than needed.