Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Doesn't DDD and CQRS/ES break the persistence agnosticity of DDD?

The Domain model in DDD should be persistence agnostic.

CQRS dictates me to fire events for everything I wan't to have in my read model. (And by the way to split my model into a write model and at least one read model).

ES dictates me to fire events for everything that changes state and that my aggregate roots must handle the events itself.

This seems not to be very persistence agnostic to me.

So how could DDD and CQRS/ES be combined without heavy impact of this persistence technology to the domain model?

Is the read model also in the DDD domain model? Or outside of it?

Are the CQRS/ES events the same as DDD domain events?

Edit:

What I took out of the answers is the following:

Yes, for ORM the implementation of the domain model objecs will differ than that with using ES. The question is the false way around. First write the domain model objects, then decide how to persist (more event like => ES, more data like => ORM, ...).

But I doubt that you will ever be able to use ES (without big additions/changes to your domain objects) if you did not make this decision front up, and also to use ORM without decide it front up will cause very much pain. :-)

like image 803
Holger Thiemann Avatar asked Jan 08 '23 01:01

Holger Thiemann


2 Answers

Commands

Boiled down to its essence, CQRS means that you should split your reads from your writes.

Typically, a command arrives in the system and is handled by some sort of function that then returns zero, one, or many events resulting from that command:

handle : cmd:Command -> Event list

Now you have a list of events. All you have to do with them is to persist them somewhere. A function to do that could look like this:

persist : evt:Event -> unit

However, such a persist function is purely an infrastructure concern. The client will typically only see a function that takes a Command as input and returns nothing:

attempt : cmd:Command -> unit

The rest (handle, followed by persist) is handled asynchronously, so the client never sees those functions.

Queries

Given a list of events, you can replay them in order to aggregate them into the desired result. Such a function essentially looks something like this:

query : target:'a -> events:Event list -> Result

Given a list of events and a target to look for (e.g. an ID), such a function can fold the events into a result.

Persistence ignorance

Does this force you to use a particular type of persistence?

None of these functions are defined in terms of any particular persistence technology. You can implement such a system with

  • In-memory lists
  • Actors
  • Event Stores
  • Files
  • Blobs
  • Databases, even
  • etc.

Conceptually, it does force you to think about persistence in terms of events, but that's no different than the approach with ORMs that force you to think about persistence in terms of Entities and relationships.

The point here is that it's quite easy to decouple a CQRS+ES architecture from most implementation details. That's usually persistent-ignorant enough.

like image 114
Mark Seemann Avatar answered Jan 10 '23 14:01

Mark Seemann


A lot of the premises in your question present as very binary/black-and-white. I don't think DDD, CQRS, or Event Sourcing are that prescriptive--there are many possible interpretations and implementations.

That said, only one of your premises bother me (emphasis mine):

ES dictates me to fire events for everything that changes state and that my aggregate roots must handle the events itself.

Usually ARs emit events--they don't handle them.

In any case, CQRS and ES can be implemented to be completely persistence agnostic (and usually are). Events are stored as a stream, which can be stored in a relational database, a NoSQL database, the file system, in-memory, etc. The storing of events is usually implemented at the boundaries of the application (I think of this as infrastructure), and domain models have no knowledge of how their streams are stored.

Similarly, read models can be stored in any imaginable storage medium. You can have 10 different read models and projections, with each of them stored in a different database and different format. Projections just handle/read the event stream, and are otherwise completely decoupled from the domain.

It does not get any more persistence agnostic than that.

like image 43
Phil Sandler Avatar answered Jan 10 '23 15:01

Phil Sandler