I am trying to learn more about DDD
and was going through the DomainEvents
. Let's say we have three microservices Service A
, Service B
and Service C
.
Service A
has an entity Foo
defined as below:
public class Foo : AggregateRoot
{
public string id {get; private set;}
public string name {get; private set;}
public string email {get; private set;}
}
and the Service B
is a service that depends upon the email
from Foo
while the Service C
depends on the name
from Foo
and the data is replicated from Service A
to Service B
and to Service C
whenever there is a change in the values of Foo
via a Bus
.
Guidelines about Domain Events that I came accross:
DomainEvent
data.consuming BoundedContext
knows about Producing BoundedContext
maybe share the Id otherwise share full informationDomainClasses
to represent data in EventsPrimitive types
for data in Events
Now the question that arose due to conflicting guidelines:
Does it mean that I should fire two different events when they change like
FooNameChange
andFooEmailChanged
and only use theid
along with theupdated value
as part of theEvent Payload
?
Or can I just make a single DomainEvent
called FooChanged
and take the state of the Foo
serialize it and fire the event. Then write up a handler as part of the same BoundedContext
that would take the data and drop it on the Bus
for any service subscribed to the message and the individual service decides on what actions to take based on the Id
that was attached and the event arg (the updated data).
If you need to talk across services, you should be perhaps looking for Integration Events instead of "Domain Events" From Microsoft Docs
Domain events versus integration events
Semantically, domain and integration events are the same thing: notifications about something that just happened. However, their implementation must be different. Domain events are just messages pushed to a domain event dispatcher, which could be implemented as an in-memory mediator based on an IoC container or any other method.
On the other hand, the purpose of integration events is to propagate committed transactions and updates to additional subsystems, whether they are other microservices, Bounded Contexts or even external applications. Hence, they should occur only if the entity is successfully persisted, otherwise it's as if the entire operation never happened.
As mentioned before, integration events must be based on asynchronous communication between multiple microservices (other Bounded Contexts) or even external systems/applications.
Thus, the event bus interface needs some infrastructure that allows inter-process and distributed communication between potentially remote services. It can be based on a commercial service bus, queues, a shared database used as a mailbox, or any other distributed and ideally push based messaging system.
What information you send within the integration events, it really depends. You have the following choices:
Publish the event such as FooNameChanged
, FooEmailChanged
with only Id
of Foo
. In that scenario, if your consumers need further information of what has changed, they would need to make a call your service (perhaps a REST API call). A disadvantage of this approach is that if you have many subscribers to your event then all those services would call your service to get the details of the event almost at the same time.
Publish the event with the full data (note that it is not same as your Domain) which a consuming service may need such as PerviousValue
, CurrentValue
, etc. If your payload is not huge, this can be a good option. These types of events are typically called "FAT events"