DRAFT... time-spent (45-min)

This blog post is a work in progress. If anything doesn't seem quite right, please Tweet to me at https://twitter.com/shaunluttin.

The intended audience for this post is software developers that are familiar with CQRS/ES and want a story about someone who has been using it for five weeks.

Directory Structure


Before introducing CQRS/ES, the existing system was following Domain Driven Design. The models/ directory contained Domain Models and the repositories/ directory contained interfaces for populating those models from some data store.

Adding CQRS/ES involved introducing the additional directories and also split repositories/ into command/ and queries/.

  • The infrastructure/ directory contains domain-agnostic CQRS/ES machinery.
  • The remaining directories contain domain-specific stuff such as the command/event pair CreateNewInvoice and NewInvoiceCreated.
  • The snapshotUpdateServices/ directory is somewhat unconventional and is responsible for updating the domain models when relevant events occur. It's a (temporary) concession that allows the system to continue running its queries against the domain model.

Separation of Command/Query Responsibilities

In the canonical pattern, domain commands produce domain event models, which the system writes to the event store, and queries run against view models that materialize from the event store. That means that the "write model" consists of domain event models, the "read model"  consists of view models, and that both use the event store are their source of truth.

The snapshotUpdateServices/ exists because our existing system was already performing reads and writes against the domain model. Instead of moving away from that entirely, we changed our write model to domain events, which follows the canonical pattern, and introduced the snapshotUpdateServices/, which listens to events as they occur and makes appropriate updates to the domain models. This is similar to the Materialized View pattern, with the difference that the snapshotUpdateServices/ update domain models instead of updating view-specific ones. This approach allowed us to keep our existing read system intact while migrating to CQRS/ES.