Making architectures evolvable
Self-similar patterns
As architects, we tend to gravitate towards patterns that work well at multiple scales. The same shape that reads as a simple queue in on object-oriented design will read as a message queue between services. An eventbus has the same shape as an observer.
Using these associations consciously allows us to explore and test what will become larger-scale architectures with minimum investment and the shortest possible feedback loop: We can observe the system's behavior and predict shortcomings and limitations. We can explore different implementations without the overhead and cost of full-scale engineering. By the time we need to scale out, we already know the business mechanisms and boundaries of the system, and all that's left to do is to add the technological plumbing on top.
This catalogue is a work in progress.
Patterns will be added over time. Please beware that code examples will always be simplified to serve teaching purposes, and do not reflect production-ready implementations. In particular, your own versions should include input validation, proper error handling, and satisfy security needs.
Pattern shapes
- RepositoryPersistence
- QueueFirst-in, first-out task scheduling
- StackLast-in, first-out task scheduling
- Pipes & FiltersCompose transformation pipelines
- FacadeProvide unified interface, hide details
- ActorIsolated state, message in / message out
- ProxyCall local, execute remotely
The Observer pattern implements a publish-subscribe mechanism where objects can register interest in messages generated by a subject. When a message is published, all registered subscribers are notified and can react accordingly. This pattern promotes loose coupling between components, as objects only need to know about the Subject and the messages they care about, without needing to know about their origin, or details about the mechanisms that caused them.
First, one or more observers (i.e., objects that are interested in being updated when something happens) attach() themselves to the subject (the object that will distribute the news). At any point in time, we can now call the subject's notify() method with whatever news we want to share. It will go through the list of attached observers and call their update() method to pass on the news. If we are no longer interested, we can simply detach the observer from the subject.
There are many variations and more specialized versions of this pattern, most notably in languages that allow passing functions as arguments, such as JavaScript/TypeScript. Here, we can pass a handler function instead of an object reference, which allows for even greater flexibility in naming and structuring the code - we no longer need to name the handler function as update(), but can instead use a more descriptive name that reflects the purpose of the handler.