11

In a CQRS Domain Driven Design system, the FAQ says that a saga should not query the read side (http://cqrs.nu). However, a saga listens to events in order to execute commands, and because it executes commands, it is essentially a "client", so why can't a saga query the read models?

magnus
  • 4,031
  • 7
  • 26
  • 48

3 Answers3

6

Sagas should not query the read side (projections) for information it needs to fulfill its task. The reason is that you cannot be sure that the read side is up to date. In an eventual consistent system, you do not know when the projection will be updated so you cannot rely on its state.

That does not mean that sagas should not hold state. Sagas do in many cases need to keep track of state, but then the saga should be responsible of creating that state. As I see it, this can be done in two ways.

It can build up its state by reading the events from the event store. When it receives an event that it should trigger on it will read all events it needs from the store and build up its state in a similar manner that an aggregates does. This can be made performant in Event Store by creating new streams.

The other way is that it continuously listens to events from the event store and build up state and stores it on some data storage like projections do. Just be careful with this approach. You cannot reply sagas in the same way as you do with projections. If you need to change the way you store state and want to rebuild it, make sure that you do not execute the commands that you have already executed.

user707727
  • 1,287
  • 7
  • 16
  • But what if we don't care that the data is only eventually consistent, i.e. if the value is a bit old? All commands coming via the UI are based on old data (projected data that hasn't been updated since the query). We don't have a problem with that. Personally, I think sagas could perhaps be considered somewhat like a client. PS I can understand there may be scenarios when transactionally consistent data is needed, then I fully support using the event more to keep that data up-to-date in saga. – Ashley Aitken Dec 19 '17 at 12:47
  • 2
    If a SAGA is considered a client and receives events in order / synchronous like other projections do, the projected data it uses will always be in sync. Or am I missing something? – huysentruitw Mar 20 '18 at 10:37
3

Sagas use the command model to update the state of the system. The command model contains business rules and is able to ensure that changes are valid within a given domain. To do that, the command model has all the information available that it needs.

The read model, on the other hand, has an entirely different purpose: It structures data so that it is suitable to provide information, e.g. to display on a web page.

Since the saga has all the information it needs through the command model, so it doesn't need the read model. Worse, using the read model from a saga would introduce additional coupling and increase the overall complexity of the system considerably.

This does not mean that you absolutely cannot use the read model. But if you do, be sure you understand the consequences. For me, that bar is quite high, and I have always found a different solution yet.

theDmi
  • 17,546
  • 6
  • 71
  • 138
  • 1
    OK, but what about this scenario (http://stackoverflow.com/questions/34257897/how-are-consistency-violations-handled-in-event-sourcing)? Would you recommended the event store be smartened (complicated), by allowing to be queried by arbitrary aggregate key/value pairs? – magnus Dec 15 '15 at 09:49
  • You certainly need to be able to reconstitute an aggregate from the event store to be able to verify business rules - this is what the command model is all about. Of course, this is a bit more involved than when using an ORM - this is the trade-off you made when choosing ES. – theDmi Dec 15 '15 at 10:11
  • 1
    You didn't answer the question in my comment. With the referenced SO question, there is an `Order` AR and a `Customer` AR. When the `postalAddress` of the `Customer` is updated, the saga must retrieve all unshipped `Order`s belonging to the customer in order to update the order's `deliveryAddress` (this is a 1-to-many relation in SQL terms, even though they are both ARs in DDD terms). How would you have the saga do this without accessing the read model? – magnus Dec 15 '15 at 23:33
  • I did answer your comment question: you need to be able to load these aggregates (Customer and all Orders) as part of the command model. Since there are only a few Orders in such a situation, I'd probably load them one by one. If there is a chance that there are a lot of Orders that need to be updated, then you have a design smell: You should only update one aggregate at a time - so reevaluate your aggregate boundaries in this case. – theDmi Dec 16 '15 at 07:33
2

It's primarily about separation of concerns. Process managers (sagas) are state machines responsible for coordinating activities. If the process manager want to affect change, it dispatches commands (asynchronous).

Also: what is the read model? It's a projection of a bunch of events that already happened. So if the processor cared about those events... shouldn't it have been subscribing to them all along? So there's a modeling smell here.

Possible issues:

The process manager should have been listening to earlier messages in the stream, so that it would be in the right state when this message arrived.

The current event should be richer (so that the data the process manager "needs" is already present).

... variation - the command handler should instead be listening for a different event, and THAT one should be richer.

The query that you want should really be a command to an aggregate that already knows the answer

and failing all else

Send a command to a service, which runs the query and dispatches events in response. This sounds weird, but it's already common practice to have a process manager dispatch a message to a scheduling service, to be "woken up" when some fixed amount of time passes.

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91