Monday, December 14, 2009

CQRS: The Domain - Design Options

When using the Command-Query Responsibility Segregation architecture the domain is solely responsible for executing update behavior.

Removing the query responsibility from your domain code enables new ways of designing and implementing the domain. The only additional requirement is that your domain must raise domain events so that the query database (and other interested parties) can be kept up-to-date.

Object-Relational Mapping

Just as with a traditional three-tier architecture the domain can be persisted using an ORM tool like (n)Hibernate, JPA, etc. Since the domain no longer needs to support query operations the relationships and mappings are likely to be simpler. This will reduce the impact of the ORM tool on your domain, but won't eliminate it entirely.

Domain events must also be raised. This is most easily done with some simple infrastructure and accessed simply through a static method: DomainEvents.raise(event). This is the approach used by Udi Dahan and is explained by his Domain Events - Salvation article.

This approach has the advantage that you can reuse your knowledge of an ORM managed domain model and may also be useful when migrating from a three-tier architecture to a CQRS architecture.

The main disadvantage of this approach is that there is no guarantee that the state changes persisted by the ORM match the state changes represented by your domain events. Depending on your needs this may or may not be a problem.

Event Sourcing

Another approach is to only persist the events, not the actual domain objects. Since all state changes are represented by events, the state of your domain can be restored by reloading and replaying all applicable events. This is the approach advocated by Greg Young and combines Domain-Driven Design with Event Sourcing. See my blog on the design of the event store for some implementation considerations and there is also an example application using this approach.

With this approach, your Aggregates become event sources and consistency boundaries that can be loaded and stored independently.

Advantages include a domain model free of any ORM mapping and a fully consistent audit log. Events are also guaranteed to be consistent with your domain model, since your domain is loaded using the same events that are send out to the query model.

A disadvantage is that relationships between aggregates can be somewhat harder to manage than with a traditional ORM mapped domain model.

Other Options

When using Event Sourcing the persistence concerns are almost completely taken out of the domain code and moved into infrastructure. This opens up the possibility of using a completely different paradigm whe implementing the domain. One example would be the use of a functional programming language to implement your domain logic.

In this case each command can be represented as a function: f :: [Event] -> [Event]. The input to the function would be the historical events already persisted and the output would be the events representing the results of executing the command. The infrastructure takes care of persisting and publishing the events, leaving the domain functions side-effect free.

No comments:

Post a Comment