// architecture
Software Architecture Patterns
A practitioner's tour of the architecture patterns that show up in real backend systems. Each pattern explains what it is, when to use it, the trade-offs, and how it composes with the others.
Quick Reference
- ›Monolith — one deployable; fastest to ship, hardest to scale teams
- ›Modular Monolith — bounded modules in one process; best default
- ›Microservices — independent services; high ops cost, high team scalability
- ›Event-Driven — async via events; decouples producers from consumers
- ›Hexagonal / Ports & Adapters — domain at the center, IO at the edges
- ›Clean Architecture — concentric layers; dependencies point inward
- ›CQRS — separate read and write models
- ›Event Sourcing — store events; derive state by replay
Learning Path
Recommended order
- 1.Beginner
- 2.Intermediate
- 3.Advanced
Prerequisites
- •Built at least one end-to-end app
- •Understand HTTP, DB, queues at a high level
Skills you will learn
- ✓Reasoning about coupling and cohesion at the system level
- ✓Choosing the right pattern for team size and load
- ✓Composing patterns (e.g., modular monolith + CQRS)
Estimated time
2–3 hours to read; years to master in practice.
Architecture Overview
Architecture
Hexagonal (Ports & Adapters) Architecture
Monolith
One deployable, one database, one process.
All features in a single codebase and runtime. Simple to develop, deploy, and reason about — the right default for small teams and early-stage products.
Pros
- +Fastest to ship
- +Easy local dev
- +Atomic transactions
- +One observability stack
Cons
- –Coupled deploys
- –Hard to scale teams past ~15 engineers
- –Single-language lock-in
Best for: MVPs, small teams, products with unclear domain boundaries.
Modular Monolith
Bounded modules behind hard interfaces, one runtime.
Split the codebase into modules with clear, enforced boundaries (no cross-module DB access). You get most of the dev-velocity of a monolith with a clean upgrade path to microservices.
Pros
- +Strong boundaries without ops cost
- +Refactor inside a module freely
- +Easy upgrade path
Cons
- –Requires discipline
- –Boundaries decay without tests
Best for: Teams of 5–25 engineers wanting structure without distributed-system tax.
Microservices
Independently deployable services around business capabilities.
Each service owns its data and lifecycle. Enables team autonomy at scale, at the cost of distributed-system problems (latency, consistency, observability).
Pros
- +Independent deploys
- +Per-service tech choices
- +Team autonomy at scale
Cons
- –Distributed-system complexity
- –Heavier ops
- –Eventual consistency by default
Best for: Organizations with 30+ engineers and clear bounded contexts.
Event-Driven Architecture
Services communicate via asynchronous events.
Producers emit events to a broker (Kafka/RabbitMQ); consumers react. Loose coupling, natural fan-out, but harder to reason about end-to-end flow.
Pros
- +Loose coupling
- +Natural retry/backpressure
- +Audit trail of intent
Cons
- –Eventual consistency
- –Hard to debug across consumers
- –Schema governance is critical
Best for: High-throughput pipelines, cross-team integrations.
Hexagonal (Ports & Adapters)
Domain at the center, IO at the edges.
The domain knows nothing about HTTP, DB, or queues. Adapters translate between the outside world and domain ports.
Pros
- +Easy to test the domain in isolation
- +Swap infra without touching business logic
Cons
- –More boilerplate up front
Best for: Long-lived business systems with non-trivial rules.
Clean Architecture
Concentric layers, dependencies point inward.
Entities → Use Cases → Interface Adapters → Frameworks. Frameworks depend on use cases, never the other way around.
Pros
- +Framework-independent core
- +Testable use cases
Cons
- –Layer overhead in small apps
Best for: Apps you expect to outlive their framework.
Layered Architecture
Classic controller → service → repository.
The default in most Spring/Django/Rails apps. Good enough for most CRUD-heavy services.
Pros
- +Familiar
- +Easy to onboard
Cons
- –Anemic domain model risk
- –Cross-cutting concerns leak
Best for: Most CRUD-heavy backends.
CQRS
Separate read model from write model.
Commands mutate state; queries hit denormalized read models optimized for the UI. Often paired with event sourcing.
Pros
- +Reads scale independently
- +Write model stays clean
Cons
- –Eventual consistency between read and write
- –More moving parts
Best for: Read-heavy systems with complex query shapes.
Event Sourcing
Store every state change as an immutable event.
State is derived by replaying events. Powerful for audit, debugging, temporal queries — but a steep learning curve.
Pros
- +Full audit trail
- +Time-travel debugging
- +Natural fit with CQRS
Cons
- –Schema evolution is hard
- –Operational complexity
Best for: Banking, ledger, trading, regulated domains.
Pattern Selection Matrix
| Pattern | Team Size | Ops Cost | Best Trade-off |
|---|---|---|---|
| Monolith | 1–10 | Low | Speed of delivery |
| Modular Monolith | 5–25 | Low–Med | Structure + speed |
| Microservices | 30+ | High | Team autonomy |
| Event-Driven | Any | Med–High | Loose coupling |
| Hexagonal | Any | Low | Testability |
| CQRS + ES | 10+ | High | Audit + read scale |
Common Mistakes
- !Reaching for microservices before you've felt monolith pain.
- !'Distributed monolith' — services that must deploy together.
- !Event-driven without schema governance (Avro/Protobuf + registry).
- !Hexagonal architecture as ceremony — layers without real adapters.
Production Tips
- ★Start as a modular monolith; extract services only along proven seams.
- ★Define bounded contexts before drawing service boundaries.
- ★Pair event-driven flows with the Outbox pattern for atomic publish.
- ★Document one Architecture Decision Record (ADR) per significant choice.
Further Reading
Frequently Asked Questions
Should I start with microservices?
No. Start with a modular monolith and extract services only when team or scale requires it.
Is CQRS overkill for CRUD apps?
Yes — use it when read and write shapes meaningfully diverge or read load dwarfs write load.
Hexagonal vs Clean Architecture?
They're close cousins. Hexagonal emphasizes ports/adapters; Clean adds explicit layering. Pick one vocabulary per team.
