Tonight after midnight in Barcelona, I was in a conversation about CQRS and how using Domain Events or not using them can impact the Command Model and the projection of changes into the Query Model. This led to some interesting points raised regarding the intent of Domain Events and some basic “rules” on how they should be modeled given what can happen in a domain model. I don’t know why, but perhaps because of the unexpected nature of the conversation and where it led, I think of the conversation and Domain Events in terms of, “and then this happened.” I’ll explain.
Our Command Model, as in the Domain Model, has a concept named Warble. You can tell by the enigma of the name itself and the inherent difficulty in understanding a Warble, that this is a complex model. I will try to help you understand the nature of Warbles along the way, but I suggest not getting lost in the details and just observe what can happen to them. For example, I can tell a Warble to do something like,
warble.doThis(parameter)
I know what you are thinking, and I understand why. I mean, wow, that really blows your hair back. Warbles can do this, and when they do this, the state of the Command Model changes. The trick with CQRS is to cause the Query Model to reflect the changes just made to the Command Model. How? There are at least two basic ways,
- Either the Warble can emit one or more Domain Events, as if to say “and then this happened,” and the Query Model can be updated by projecting the Domain Events, or
- The entire state of the Warble that was changed can be used by the Query Model to project the changes
If you do the former, the meaning of what happened should be self evident in the Domain Events. For example, ThisHappended would be a strong name for such a Domain Event. If you don’t provide a good name, such as in an attempt to say everything, as in WarbleUpdated, you have actually said nothing. The following is along the lines of strong name,
emit(ThisHappended.with(parameter))
If you use the latter approach, then you should provide some sort of indication as to why the Warble state changed. You could create an attribute on the Warble instance named reasonForChange or simply operation. For example,
operation = "doThis"
You can review some code in vlingo/symbio that shows how entity full state is dispatched for Query Model projection.
I know that some will argue that one of the above two ways is better than the other. For any given set of situations, you’d be right. So tradeoffs happen. Maybe it’s more important to think about modeling Warbles rather than arguing about which is a better way to project state changes into the Query Model. Ask yourself, do Domain Events such as ThisHappended strengthen to model? If so, you probably have your answer.
Now that we’ve conclude that Domain Events strengthen the model, should the model be Event Sourced? I am simply not going there very late on a Friday night (e.g. very early on a Saturday morning). But I think the wrong approach is to first say “of course Domain Events strengthen the model” just so you can reach the technical conclusion that you cherish “and everybody knows that Event Sourced models are the single source of truth and therefore we are using Event Sourcing.” Wouldn’t it be much better to walk through some concrete scenarios to prove that Domain Events strengthen the model? Seems so.
When our discussion reached this point, the question went something like this: When would a Warble ever emit more than one event? For example, if a Warble could do two things independently of each other, but sometimes it could do both of those things at the same time, would there be one Domain Event emitted, or two? So I came up with this scenario, where you can do at least three things with a Warble. You can,
- Tell a Warble to doThis
- Tell a Warble to doThat
- Tell a Warble to doThisThat
Let’s assume that when a Warble handles scenario 1, it does this:
warble.doThis(parameter) { ... emit(ThisHappended.with(parameter)) }
And when it handles scenario 2, it does this:
warble.doThat(parameter) { ... emit(ThatHappended.with(parameter)) }
Then the question becomes, which of the following models is better (and notice I didn’t say “correct”)? This one,
// model 1 warble.doThisThat(parameter) { ... emit(ThisHappended.with(parameter), ThatHappended.with(parameter)) }
Or this one,
// model 2 warble.doThisThat(parameter) { ... emit(ThisThatHappended.with(parameter)) }
My point was that it’s not your decision or my decision to determine which is best. It depends on how the business reasons about this scenario. Take these two cases, which are the ones that I presented as possibilities:
- If doThisThat is a convenience or shortcut for doThis and doThat, then the Warble would emit two Domain Events; that is, the user could be required to take two separate steps, but we’ve found in some cases it’s best to enabled them to take one step
- If doThisThat is seen by the business as a distinct operation (behavior), then the Warble would emit one Domain Event; that is, the doThisThat is not just a convenience but performed atomically because that’s what the business situation calls for right now
In either case, the indication of what happened according to the business, and provisioning changed state happening(s) into the Query Model, are well covered. The point of open source XOOM is to give you the tools to support whatever way you decide to implement using Reactive DDD.