The granularity of the events is always a tricky question, @Jan_Rahm.
I tend to ask myself, the team, and the business/domain experts, is is there value in these fine-grained events?
For example, perhaps some part of the business is very much interested solely in the WindowsAdjustedEvent.
If that’s the case, it will be beneficial actually to have that event.
As if it wasn’t published, there will be some larger encapsulating event requiring the event handler to branch (if/else statements).
Both approaches have their pros and cons, of course.
But, leading in this decision should be what provides the most value to the application.
To at least give you some guidelines, let me provide the pros and cons of the options you provide:
Very granular events will allow granular event handlers. Furthermore, it is overly clear what actually happened, as the event’s name describes it clearly. However, you will need a lot of them, and you would need to figure out some milestone event to be able to trigger specific behavior (like only sending a fully updated Vehicle update through the subscription query.
You will significantly minimize the number of event types. However, every listener will need to switch between the enumeration within the event. Furthermore, the event loses its described intent entirely, as it’ll just be some “VehicleUpdatedEvent.” CUD-like events typically don’t provide a lot of value to the event store.
The same pointers as with option two apply. Fewer events, but less clarity on the event store. Furthermore, a lot of branching inside the event handlers, making them less to the point.
Similar argumentation as with options two and three apply here. But, there’s another argument against using larger POJOs/value objects inside your events, which is the schema. The serialized format of the event is a contract you will need to uphold until the end of time. The more complex the XML/JSON/etc is, the more effort you will need to put into writing upcasters. Note that this contract point also applies to solutions two and three, albeit less stringent.
If I gave my opinion, I would go for more granular events.
I personally think that having clearly named events as opposed to coarse-grained XyzUpdatedEvents.
Granted, the entire team should be on the same page as what the granularity should be.
Now let me move to your final question:
The updates of the subscription query do not require you the send the entire Vehicle per se.
You can let the subscription query update smaller portions of the Vehicle if needed.
Unsurprisingly I agree with everything Steven wrote. I just wanted to add my 2 cents to the following parts:
You don’t have to stick to extremes such as one event per car vs. one event per every part. You can slice and dice those in any other way that makes sense to the actors involved. For example, you may want to further group events into, say, “car engine”, “car body”, “car chassis”, “car interior”, etc., because those are emitted and/or handled by different teams. Then for each group, you can decide what level of granularity it needs.
I assume you mean the update of the entire car. If so, then how do you know it in the actual domain (software aside)? To use your example, how do you know after how many door or widow updates the car update is complete? I guess you would need some indication that a car update process has started and then has completed. That could be automated (for example, based on “car entered/left the garage” events) or manual (someone has to say: “we started now” and “we are done”). Back to the software, you can implement this as a process(saga). Suppose you have split your events into groups (as described above). In that case, you may start a process with updateCar event that says “it needs engine, body, and chassis work” and then complete it when you receive EngineWorkDone, BodyWorkDone, and ChassisWorkDone events.
I have one more question about one situation I am unsure how to handle:
I will send the command to update 5 fields from the exampled Vehicle in the first post. Should I create an event update for the fields which do not differ? For example doors=5 in the command and doors=5 is already in DB/Aggregate variable.
Events inform about something that has happened, in this case the state of a Vehicle aggregate was updated by changing the number of doors. Since the correct number of doors is already known by the Vehicle aggregate, no state needs to be changed. The command is handled by the aggregate “silently” without publishing an updated event, because no state updates actually happened.