Best practices, guidance, and anti-patterns to avoid when building an application following CQRS/ES principles.
Services share schema and contract, not class.
Don't share types (i.e. .NET classes compiled into assemblies) as a way to express contracts in a messaging system.
Do define and share a schema and contract. Publish it for consumers to build their own internal representation of your data.
- At the Boundaries, Applications are Not Object-Oriented by Mark Seemann
- Data/Contract Coupling in Messaging by Clemens Vasters.
Do use simple types in domain events (strings, numbers, lists).
Don't use value objects in events as events are immutable, whereas the definition of value objects may change over time.
Value object's are immutable, but their definition (class or schema) can be changed at any time by developers. Doing so will inadvertently break any existing events unless an explicit upgrade strategy is implemented. Using simple types in domain events alleviates this somewhat because developers should understand that changing an event will affect existing stored events.
Don't expose domain events outside of the service, or bounded context, that creates them.
Do use integration events.
An external integration event is a public event produced by a bounded context which may be interesting to other domains, applications or third party services. The purpose of integration events is to explicitly propagate committed transactions and updates to additional subsystems. You may convert and publish a domain event to external services, as an integration event, after it has been committed.
Using integration events makes it easier to change internal domain events, while allowing external integration events to provide a stable API to consumers. It also allows a service to only expose a subset of its domain events as integration events. Integration events can be enriched with additional data, such as from a read model projection, that is relevant to external uses.
- Internal vs External Models
- Domain Events vs. Integration Events
- Domain events versus integration events
Don't share data between projections.
Do use autonomous projectors responsible for their own data. Projectors should run independently, allowing them to track their own progress and be rebuilt as required.
Don't enforce consistency rules in your read model projection.
Do follow Postel's robustness principle:
Be conservative in what you send, be liberal in what you accept.
Pull requests gladly accepted for your best practices, advice, and guidance.