Architecture Patterns with Python

Architecture Patterns with Python

Enabling Test-Driven Development, Domain-Driven Design, and Event-Driven Microservices

Harry Percival, Bob Gregory

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. We also use events for communicating between aggregates so that we don’t need to run long-running transactions that lock against multiple tables. 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 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.
4070