Problem
Business operations have side effects in other modules, such as sending email or updating inventory. Calling those modules directly couples the core domain to everything that cares about the change.
Also known as: domain-events, business-event
Capture something meaningful that happened in the domain as an immutable fact that other parts of the system can react to.
Business operations have side effects in other modules, such as sending email or updating inventory. Calling those modules directly couples the core domain to everything that cares about the change.
A domain event states a fact: it already happened and cannot be undone. Names use past tense and business language, for example InvoicePaid rather than UpdateInvoiceStatus. Events differ from commands, which ask for something to happen and may be rejected.
The aggregate raises an event while enforcing its rules. The application layer saves the aggregate and then dispatches the collected events.
class Order:
def place(self):
self.status = "PLACED"
self.events.append(OrderPlaced(self.id, self.total))
# after commit
for e in order.pull_events():
bus.publish(e)
Order.place() -> OrderPlaced -> [Email handler]
-> [Inventory handler]
-> [Outbox -> Broker -> other contexts]
Reliable delivery to other services needs the transactional outbox: write the event into an outbox table in the same transaction as the state change, then a relay publishes it.
Low in-process; broker needed for cross-service use
Medium — monitor delivery and handler failures
Medium — indirect flow of control
Event published but transaction rolled back, announcing a false fact
Lost events when the process crashes between commit and dispatch
Duplicate delivery causing repeated side effects