Domain-Driven Design
Domain Events
Something that happened in the domain, said in the domain’s own words.
A domain event records something significant that has already happened inside a model. The aggregate decides the fact. When handlers run relative to persistence is a separate transaction decision.
Past tense helps, but grammar is not the definition. The event must describe a completed domain decision rather than request the next one.
Record the fact with the change
Microsoft’s eShop Order aggregate records a domain event when a submitted order moves to Awaiting Validation:
public void SetAwaitingValidationStatus()
{
if (OrderStatus == OrderStatus.Submitted)
{
AddDomainEvent(
new OrderStatusChangedToAwaitingValidationDomainEvent(
Id, _orderItems));
OrderStatus = OrderStatus.AwaitingValidation;
}
}
The aggregate records the event in the same method that changes its state. eShop collects those events on the entity and dispatches them before SaveChanges. Handlers using the same context can therefore join the commit. They do not have to inspect old and new columns later and guess what happened.
The event contains the order id and items needed by reactions in this model. It does not expose an Entity Framework change tracker or an orders.status column.
Next work is a command
MiniVerine makes another distinction. Its cascade code turns a successful handler result into outgoing message bodies:
switch (value)
{
case null:
case Saga:
case Domain.Envelope.Envelope:
return;
default:
outgoing.Add(value);
return;
}
An outgoing message is not automatically a domain event. ChargePayment asks for work and is a command. PaymentCharged reports a completed fact and could be an event. MiniVerine uses those names in its planned Helpdesk conversation, but the types and running pipeline do not exist yet.
A command may cause a domain event. A domain-event handler may decide to issue another command. Keeping the names and roles separate stops “event-driven” from meaning “everything is a message”.
The event stops at the context
The eShop event belongs to its Ordering model. Sending the same fact across a bounded context requires an integration contract. The other context should not depend on Ordering’s aggregate internals merely because both use messages.
Integration Events sit on the hub as related, later. They are not a page in this notebook yet.