Don't DDD and CQRS / ES violate DDD agnostic?

The domain model in DDD must be agnostic.

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

ES dictates that I fire events for anything that changes state and that my aggregate roots should handle the events themselves.

It seems to me that this is not very relevant for me.

So, how can DDD and CQRS / ES be combined without this persistence technology dramatically affecting the domain model?

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

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

Edit:

From the answers I got the following:

Yes, for ORM the implementation of the objecs object model will be different from the implementation using ES. The question is the wrong way. Write domain model objects first, then decide how to persist (more events like => ES, more data like => ORM, ...).

But I doubt that you will ever be able to use ES (no big additions / changes to your domain objects) if you didn't make that decision, and also use an ORM without deciding that it would happen, a pain. :-)

+3


source to share


3 answers


Commands

According to its essence, CQRS means that you should split your reads into your records.

Typically, a command enters the system and is processed by some function, which then returns zero, one, or many events resulting from this command:

handle : cmd:Command -> Event list

      

You now have a list of events. All you have to do is save them somewhere. A function that can do this might look like this:

persist : evt:Event -> unit

      

However, such a permanent function is purely infrastructural. Usually the client sees only a function that accepts a command as input and returns nothing:

attempt : cmd:Command -> unit

      

The rest ( handle

followed by persist

) is processed asynchronously, so the client never sees these functions.

Inquiries



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

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

      

Given a list of events and a search target (for example, an identifier), such a function can collapse events into a result.

Keeping ignorance

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

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

  • Lists in memory
  • Actors
  • Event Stores
  • Files
  • Blobs
  • Databases, even
  • and etc.

Conceptually, this forces you to think of persistence in terms of events, but it is no different from the ORM approach, which forces you to think of persistence in terms of entities and relationships.

The point is, it's pretty easy to decouple the CQRS + ES architecture from most of the implementation details. This is usually persistently ignorant.

+6


source


Many spaces in your question are presented as very binary / black and white. I don't think DDD, CQRS or Event Sourcing are prescriptive - there are many possible interpretations and implementations.

However, only one of your rooms bothers me (emphasis mine):

ES dictates that I fire events for anything that changes state and that my shared roots should handle the events themselves.

Usually ARs issues events - they do not process them.



In any case, CQRS and ES can be implemented as completely agnostic (and usually). Events are stored as a stream, which can be stored in a relational database, NoSQL database, in the file system, in memory, etc. Event persistence is usually done at the boundaries of the application (I think of this as infrastructure) and the domain models don't know how their streams are stored.

Likewise, read models can be stored on any medium imaginable. You can have 10 different models and predictions to read, each stored in a different database and in a different format. Predictions just process / read the stream of events and are otherwise completely domain-separate.

He doesn't get more agnostic perseverance than that.

+3


source


Not sure how orthodox this is, but the model of an existing event as derived from the source, I have something like this that might illustrate the difference. (C # example)

public interface IEventSourcedEntity<IEventTypeICanRespondTo>{
    void When(IEventTypeICanRespondTo event);
}

public interface IUser{
    bool IsLoggedIn{get;}
}

public class User : IUser, IEventSourcedEntity<IUserEvent>{
     public bool IsLoggedIn{get;private set;}

     public virtual void When(IUserEvent event){
           if(event is LoggedInEvent){
               IsLoggedIn = true;
           }
     }
}

      

A very simple example - but you can see here that how (or even IF) the event is saved is outside the domain object. You can easily do this through the repository. Likewise, CQRS is respected because the way I read the value is different from how I set it. For example, say I have multiple devices for a user and only they want them to be logged in when there are more than two there?

public class MultiDeviceUser : IUser, IEventSourcedEntity<IUserEvent>{
     private IEnumerable<LoggedInEvent> _logInEvents = . . . 
     public bool IsLoggedIn{
         get{
              return _logInEvents.Count() > MIN_NUMBER_OF_LOGINS;
         }
     }

     public void When(IUserEvent ev){
          if(ev is LoggedInEvent){
             _logInEvents.Add(ev);
          }
     }
}

      

For the calling code, your actions are the same.

var ev = new LoggedInEvent();
user.When(ev);

if(user.IsLoggedIn) . . . . 

      

0


source







All Articles