Marcin Marcinkowski
Marcin Marcinkowski

Reputation: 55

Is my understanding of CQRS/ES correct?

I’m not often writing things on StackOverflow but I wanted to share my thoughts regarding my understanding of CQRS/ES implementation. I was confused a lot by responsibilities of different components and I think I finally got my head around the topic. Would be nice if you could tell me if my understanding is correct.

Step by step:

  1. User create and trigger a Command which is an object representing minimum value needed to perform the requested change in the domain. The command has assigned handler function. In a scenario with CommandBus, the separated Dispatcher will trigger a CommandHandler to perform an action. The CommandBus might be implemented using any messaging system, database or might just work only in a memory.

  2. The responsibility of CommandHandler is to validate command, then create and send one or more Events to EventStore according to business rules.

  3. Command can be called on Aggregate. The Aggregate is an object that consists of one or more Entities and ValueObjects. The purpose of Aggregate is to make possible to validate command not only by itself but also in context of "Aggregated" state.

  4. The EventStore might be implemented using messaging system or database. Events are always named with a past-participle verb, such as OrderConfirmed. They are consumed by EventHandlers.

  5. The EventHandler is a function responsible for „projection” which means actual saving stuff in database. It can also perform other actions like for example sending emails, everything depends on the initial purpose of the Event.

Please correct me if I'm wrong in any point. I still have few more questions.

  1. In a case when CommandHandler is generating multiple events to save multiple objects in a database can I assign them the same UUID that later I will also save in all projected objects?

  2. Can EventHandler trigger another commands or create another events? What is the best practice in that case?

Thanks for the answers.

Upvotes: 1

Views: 221

Answers (2)

Dmitry S.
Dmitry S.

Reputation: 8503

A command is a message containing all the necessary information to describe an operation that needs to happen.

An associated command handler calls the command on an aggregate root. The aggregate root validates the command and only then dispatches one or more events. Event handlers are responsible for projecting data into the read store. Sagas or state processors can be used to subscribe to events and publish other messages.

Validation needs to happen in the aggregate root. Command handler is another place where you could place simple data validation. However, event handlers are not the place for data validation. If you place validation in event handlers, a command would execute successfully but you would end up with eventually inconsistent data in the read store and any systems that subscribe to events.

Events do represent something that was dispatched by a valid command/aggregate root in the past. Every event must be considered valid because it will have to be replayed by the corresponding aggregate root once you reload the data. Business logic could change over time in the aggregate root but it should not affect event handlers. Multiple versions of events and corresponding handlers of the same type can be used to achieve that.

All the events, including the information about the aggregate root (type, UUID, etc) should be stored in the event store. The event store must be consistent and durable since events are the source of data.

I hope this information gives you a picture of how event handling works. Let me try to answer the questions now.

  1. All those events (typically) should have the same aggregate root ID and type (namespace + class), so they can be replayed by the corresponding aggregate root instance. They should also have a version number that determines the order they happen in.

  2. I covered this in the second paragraph.

Upvotes: 3

VoiceOfUnreason
VoiceOfUnreason

Reputation: 57229

a Command which is an object representing minimum value needed to perform the requested change in the domain

Not quite right. It's a message, or at least a representation of a message, not an "object". Applications aren't object oriented at the boundaries, neither are domain models.

The responsibility of EventHandler is to validate command, then create and send one or more Events to EventStore according to business rules.

That's a bit confused - you may have meant command handler there

Command can be called on Aggregate.

Very close - it's a bit more correct to say that the command is called on the Aggregate Root. In the original Evans formulation, an aggregate would designate one entity within its boundary to act as the aggregate root, which is to say it would be the one point of access for all commands and queries.

In the CQRS literature, commands and queries are treated separately; the aggregate, and its root, are used for commands only.

The EventStore might be implemented using messaging system or database.

EventStore is playing the role of the book of record; which means that it needs to support a minimum durability guarantee - nobody should be allowed to see an event until you are "sure" that the event will be available to reload the aggregate on restart.

Events are always named with a past-participle verb, such as OrderConfirmed. They are consumed by EventHandlers.

Events, like commands, are just messages; they can be consumed any way you like.

The EventHandler is a function responsible for „projection” which means actual saving stuff in database.

"Projection" is usually understood to mean creating a non authoritative representation of some history of events (produced by one or more sources). The motivation is that there is often significant work to do in creating a representation from a history; if you are trying to support low latency queries - and are comfortable with some interval between when a change is made and when a change is visible, you can improve the latency by creating a data structure that is more amenable to fast queries (or, alternatively, just preload a cache with a representation of the answer to the query).

Note: non authoritative is important. This database is not the book of record, the event store is.

In a case when CommandHandler is generating multiple events to save multiple objects in a database can I assign them the same UUID that later I will also save in all projected objects?

This question isn't clear. The projected objects are going to be constructed from events, so you would normally expect that any UUID in a projected object will be something that you derive from data in the events.

Also, it's typically not the command handler that is generating the events -- that's a responsibility of the domain model; which means that it is happening behind the aggregate root.

Can EventHandler trigger another commands or create another events? What is the best practice in that case?

One common pattern is a process manager, which is a state machine: you pass events to it to transition from one state to another, and you query it to find out which commands should not be dispatched.

Upvotes: 1

Related Questions