Part 0 · 2 chapters · ~15 min
Patterns Beyond the Frontend
The pattern vocabulary of system design: hexagonal architecture, modular monoliths, microservices, BFFs, anti-corruption layers and the strangler fig; then pub-sub, outbox and inbox, sagas, stability patterns, CQRS and event sourcing, and data scaling patterns, each with its context, cost and source.
1
Architectural and integration patterns
The patterns below shape whole systems. Each has a context where it is right and a cost you pay for using it. The vocabulary matters because design reviews are carried out in it.
| pattern | use when | avoid when | source |
|---|---|---|---|
| hexagonal / ports and adapters | domain rules must outlive providers and frameworks | the app is a thin CRUD layer | Alistair Cockburn; Clean Architecture |
| modular monolith | one or a few teams, a young product | teams are blocked on each other's deploys | common practice; Shopify's writing |
| microservices | many teams, different scaling needs | no platform team, unclear domain boundaries | Sam Newman, Building Microservices |
| BFF | several client types with different needs | one client, simple APIs | Phil Calçado / SoundCloud |
| anti-corruption layer | integrating a model you do not control | never: always wrap foreign models | Eric Evans, DDD |
| strangler fig | replacing a live legacy system | the old system can simply be switched off | Martin Fowler |
ARCHITECTURAL AND INTEGRATION PATTERNS
how systems are shaped and how their parts talk
swipe the figure sideways, or tap expand for full screen
1/6
Layered / hexagonal
Layered and hexagonal (ports and adapters): the domain core defines interfaces (ports) for what it needs; adapters implement them for specific technologies. Dependencies point inward, so the core can be tested without a database and a provider can be swapped without touching business rules.
2
Messaging, resilience and data patterns
| pattern | problem it solves | where it is covered in depth |
|---|---|---|
| pub-sub / event-driven | coupling between producers and consumers | Distributed Systems part 7 |
| outbox and inbox | the dual write; duplicate delivery | Distributed Systems parts 6 and 8 |
| saga | transactions across services | Distributed Systems part 8 |
| timeout, retry with backoff, circuit breaker, bulkhead | cascading failure from a sick dependency | this course part 9 (Release It!) |
| CQRS, event sourcing | conflicting read and write needs; auditability | Trust part 8, this course part 1 |
| cache-aside, replicas, sharding | read and write scale | Distributed Systems parts 3 and 4, Cloud part 5 |
code
// a circuit breaker in 25 lines: fail fast instead of piling onto a sick dependency
class Breaker {
private failures = 0; private openUntil = 0;
constructor(private threshold = 5, private coolMs = 10_000) {}
async call<T>(fn: () => Promise<T>, fallback: () => T): Promise<T> {
if (Date.now() < this.openUntil) return fallback(); // open: do not even try
try { const r = await withTimeout(fn(), 2_000); this.failures = 0; return r; }
catch (e) {
if (++this.failures >= this.threshold) this.openUntil = Date.now() + this.coolMs; // trip
return fallback();
}
}
}
const kyc = new Breaker();
const status = await kyc.call(() => vendor.check(bvn), () => ({ state: 'PENDING_REVIEW' })); // degraded, designedMESSAGING, RESILIENCE AND DATA PATTERNS
how parts communicate asynchronously, survive each other's failures, and keep data consistent
swipe the figure sideways, or tap expand for full screen
1/6
Publish-subscribe and event-driven
Publish-subscribe and event-driven architecture: services publish facts about what happened; consumers subscribe. Producers do not know or wait for consumers, which decouples teams and absorbs load, at the cost of flows that are harder to trace without good tooling.