Single Responsibility Principle
Rule of thumb:
Si la descripción de tu función incluye palabras como “then” o “and”, es posible que estés rompiendo SRP
One formulation of the SRP is that each class should have only a single reason to change. When we switch from email to SMS, we shouldn’t have to update our allocate() function, because that’s clearly a separate responsibility.
Domain Events and Message Bus
- El modelo no sabe de emails, SMS, etc
- El modelo registra Eventos
- El Message Bus responderá a estos Eventos
- Los Eventos son Value Objects
The Message Bus Maps Events to Handlers
Message Bus says: When I see this event, I should invoke the following handler function.” In other words, it’s a simple publish-subscribe system.
Trade-offs
| Pros | Cons |
|---|---|
| Event handlers are nicely decoupled from the “core” application logic | The message bus is an additional thing to wrap your head around; the implementation in which the unit of work raises events for us is neat but also magic. |
| Domain events are a great way to model the real world | That hidden event-handling code executes synchronously |
Recap
Events can help with the single responsibility principle
Code gets tangled up when we mix multiple concerns in one place. Events can help us to keep things tidy by separating primary use cases from secondary ones.
A message bus routes messages to handlers
You can think of a message bus as a dict that maps from events to their consumers. It doesn’t “know” anything about the meaning of events; it’s just a piece of dumb infrastructure for getting messages around the system.
Option 1: Service layer raises events and passes them to message bus
The simplest way to start using events in your system is to raise them from handlers by calling bus.handle(some_new_event) after you commit your unit of work.
Option 2: Domain model raises events, service layer passes them to message bus (Fat models)
The logic about when to raise an event really should live with the model, so we can improve our system’s design and testability by raising events from the domain model. It’s easy for our handlers to collect events off the model objects after commit and pass them to the bus.
Option 3: UoW collects events from aggregates and passes them to message bus
Adding bus.handle(aggregate.events) to every handler is annoying, so we can tidy up by making our unit of work responsible for raising events that were raised by loaded objects. This is the most complex design and might rely on ORM magic, but it’s clean and easy to use once it’s set up.