What kind of granularity is recommended in reactive programming when publishing state change events?
I'm currently developing an application using reactive programming. Every entity creation or modification in the system publishes an event and no two entities can be created/modified within the same use case.
In this situation, there are entities that have to be created based on specific states of other entities. For example, there is an entity Slot that, when created with a state IN_AUCTION, should trigger the creation of an Auction entity. This is optional, as the Slot could be created with a state AVAILABLE, in which case, no Auction should be created.
Given this is the case, my doubt is about the type of event to publish when the Slot is created. These are the options I've thought about:
- Publish a generic SlotCreatedEvent. In this case, the listener would need to verify the state of the Slot, either by adding it to the Slot event or by querying it to check its state.
- Publish a SlotInAuctionEvent or a SlotAvailableEvent. In this case, there would be a specific listener that would create the Auction without checking the Slot state but if I follow this approach I could have an explosion of events.
So, given this example, what's the expected granularity when publishing events? Should there be a specific event for each state/entity modification or just a generic one with all the information of the entity modified?
1 Answer 1
In principle, I think what's most convenient for your event handlers should be a secondary concern. One of the major advantages of events is that the event source does not need to know anything about the consumers - so you don't know what their needs are.
The question really comes down to what constitutes an event conceptually. I would argue that creating an "in auction" slot and a "not in auction" slot are not separate events - otherwise, you end up with a separate event for every combination of parameters.
However, you'll also want to avoid flooding your consumers with unnecessary information. Furthermore, I think it's wise to avoid sending the same information in different events, if possible.
In your example, you risk duplicating the status information in the SlotCreated
event and the SlotStatusUpdated
event. If you had to react to a slot being 'available', you'd have to subscribe to two events.
A possible solution to this problem might be to publish two events when you create a slot: the SlotCreated
event does not carry slot-status information, but you'll also immediately raise SlotStatusUpdated
. Then AuctionCreator
only has to subscribe to the latter. (If SlotCreated
is of no interest to anyone, you could even skip this event).
Side Note: A module that only needs to react to new slots with a given status will have to do more work in this case. I'd argue that this is a natural reflection of the more complex condition. (If there are many such subscribers, you could have an intermediary that combines both events to a new event). In any case, there may be situations where this approach is not practical, but I do think it would usually be a relatively clean solution.
I think we can summarize the above considerations into the following rules of thumb:
If two events hold the same information, extract that into a new event
Don't create a new event just to suit a subscriber's needs
Unfortunately, this still leaves one question open: do we have a StatusChanged
event or separate SlotOpenedForAuction
, SlotReserved
and SlotMadeAvailable
events?
There might be a technical reason for choosing separate events: if you have a large number of subscribers, each of which only has to handle slots in a particular status, separate events are more efficient. (Raising an event is an O(n) operation). Otherwise, you should use the domain terminology as a guide - i.e. do users talk about a slot's 'status'?
-
As of now there aren't many use cases really. There is only the handler that schedules the next slot and the one that creates the auction. My concern is about the information to include in the events, if that is a symptom of a design problem.But thinking about it, maybe what I'm missing is a middle step.An auction also requires an end time which is based on a configuration stored on the entity that triggers the slot creation in the first place. So I think I can publish the event,listen to it in the module with the config and publish a CreateAuctionCommand.Would that make sense?Kilian– Kilian02/08/2018 23:13:28Commented Feb 8, 2018 at 23:13
-
Is the
EndTime
a property of theSlot
(conceptually speaking)? If so, I'd include it in an event. Otherwise, I'm not sure if theSlotCreator
should be concerned with auctions (and their end times) at all - is there a reason theAuctionCreator
can't hold that configuration? In your suggestion, it seems theSlotCreator
is responsible for creating bothSlot
s andAuction
s.doubleYou– doubleYou02/09/2018 16:58:25Commented Feb 9, 2018 at 16:58 -
The
Slot
has an end time of its own. TheAuction
end time is defined in the configuration of theRoom
entity, that is who triggers theSlot
creation in the first place. That's why I suggested listening to theSlotCreatedEvent
so theRoom
can react to it by triggering theCreateAuctionCommand
that would include theslotId
and theAuction
configuration.Kilian– Kilian02/09/2018 18:24:32Commented Feb 9, 2018 at 18:24 -
Okay so (correct me if I'm wrong):
SlotCreator
creates a event with an expiration time (which should be part of the event). In response to that event,AuctionCreator
creates an auction with its own end time, based on some config thatAuctionCreator
holds, and possibly the expiration time of theSlotCreatedEvent
. So far so good. NowSlotCreator
andAuctionCreator
are actually both implemented inRoom
. That may be totally fine, or it may put completely separate responsibilities intoRoom
- I can't decide that without knowing the rest of the system.doubleYou– doubleYou02/09/2018 18:38:45Commented Feb 9, 2018 at 18:38 -
Room
,Slot
andAuction
are all completely separated. Each one only publishes events of itself and there are listeners reacting to them. Each listener calls a use case that contains references to components that handle each type of entity. In terms of DDD, currently each one is its own aggregate and I haven't found a reason to include any within another one. Use cases would be the domain services.Room
contains config for eachSlot
and theAuction
associated so, for me, a use case should callRoom
for each event published bySlot
andAuction
and let it decide next actionKilian– Kilian02/11/2018 14:12:45Commented Feb 11, 2018 at 14:12
Explore related questions
See similar questions with these tags.
SlotInAuctionEvent
that only creates an auction if the required conditions exist?SlotInAuctionEvent
indicates that the condition has been met. The question is if it is a good idea to have a separate event for every state in which a slot can be created vs. having one event that provides the data for the listeners to do the condition-checking.SlotFactory
checks the condition and then triggers (via event or otherwise) the creation of an auction? I think that's basically option 2 in that you need a separate event for each condition. Or did I misunderstand?