Architecture styles and evolvability
Every architecture style makes some changes cheap and others expensive. Building Evolutionary Architectures puts this down to the fact that different styles have different inherent quantum sizes, and smaller, well-bounded quanta can change independently. Fundamentals of Software Architecture (Richards & Ford) catalogues the styles and rates each one on a set of characteristics, including how evolutionary it is.
The ratings on this page are this site’s own assessment, using the lenses from coupling and architecture quanta. They aren’t copied from the book.
The lenses
Section titled “The lenses”For each style, ask:
- How many quanta does it usually have? More independent quanta means more independent change.
- What is the unit of change? A whole application, a module, a plug-in, a service, a function?
- How is coupling controlled? By discipline only, or by deployment and network boundaries?
- Who owns the data? A shared database often merges what look like separate parts into one quantum.
- What does it cost to run? Every boundary you add buys independence and costs operational complexity.
Summary
Section titled “Summary”| Style | Typical quanta | Unit of change | Evolvability (this site’s rating) | Main risk to evolution |
|---|---|---|---|---|
| Big ball of mud | 1 | Everything | Very low | No structure, so any change can ripple anywhere |
| Layered monolith | 1 | Whole application | Low | Changes to a business feature cut across every layer |
| Modular monolith | 1 | Whole application, but changes stay inside a module | Medium | Module boundaries erode without enforcement |
| Microkernel | 1 (usually) | Plug-in | Medium (high for plug-ins) | Core system and plug-in contracts are hard to change |
| Service-based | 1 to a few | Coarse-grained service | Medium–high | A shared database couples the services |
| Event-driven | Several to many | Event processor | High | Event schemas become hidden contracts; flows are hard to follow |
| Microservices | Many | Single service | Very high | Operational cost; turning into a distributed monolith |
| Serverless (AWS) | Many | Function or small service | High | Vendor coupling; logic spread across many small pieces |
The styles
Section titled “The styles”Big ball of mud (the baseline)
Section titled “Big ball of mud (the baseline)”Not really a style: a system with no deliberate structure, where everything can depend on everything. The book uses it as the degenerate case. Changes have unpredictable side effects, so teams slow down and avoid change. Most legacy systems drift toward this when nothing protects their structure.
- What it teaches: structure doesn’t maintain itself. You need fitness functions to keep a system from sliding back here.
Layered monolith
Section titled “Layered monolith”The classic presentation → business → persistence → database stack, deployed as one unit.
- Helps evolution: simple, well understood, and you can swap out a whole layer (for example, a new UI) if the layers are properly isolated.
- Hinders evolution: it’s organised by technical concern, so one business change usually touches every layer. One deployment and one database means one quantum, so the whole system has to meet the strictest requirement of any part of it.
- Fitness functions: layer dependency rules (for example ArchUnit’s layered architecture checks) so that, say, the presentation layer never calls the database directly.
Modular monolith
Section titled “Modular monolith”One deployable, but organised internally by business domain (modules), with explicit interfaces between modules.
- Helps evolution: most changes stay inside one module. You get much of the isolation people want from microservices without the distributed-systems cost. Clean modules are the best starting point for extracting services later.
- Hinders evolution: it’s still one quantum (one deployment, usually one database). The boundaries are enforced only by discipline unless you automate them.
- Fitness functions: no module reaches into another module’s internals or tables; no cycles between modules. These checks are what keep it from decaying into a ball of mud.
Microkernel (plug-in)
Section titled “Microkernel (plug-in)”A small core system plus plug-ins that add features through a defined contract. Common in products (IDEs, browsers) and in rules-heavy domains such as insurance claims or tax.
- Helps evolution: new capabilities arrive as plug-ins without changing the core. Plug-ins can be added, swapped, or removed independently.
- Hinders evolution: the core and the plug-in contract are hard to change, because every plug-in depends on them. It’s usually deployed as one unit.
- Fitness functions: plug-ins depend only on the published contract, never on core internals; contract versions stay backward compatible.
Service-based
Section titled “Service-based”A handful of coarse-grained, separately deployed domain services (often 4–12), frequently sharing one database. A pragmatic middle ground between a monolith and microservices.
- Helps evolution: services deploy independently, and boundaries are enforced by the deployment, not just by discipline. There are far fewer moving parts than with microservices.
- Hinders evolution: a shared database makes the services one quantum in practice; a schema change can affect several of them.
- Fitness functions: each service touches only its own tables or schema; services don’t call each other’s internals.
- AWS note: this maps well to a few ECS services or Lambda-backed APIs over one Aurora cluster, with a separate schema per service as a first step toward splitting the data.
Event-driven
Section titled “Event-driven”Components communicate by publishing and reacting to events, usually through a broker. Richards & Ford describe two topologies: broker (events flow freely between processors) and mediator (an orchestrator coordinates the steps).
- Helps evolution: asynchronous communication removes the synchronous dynamic coupling that merges services into one quantum. New consumers can subscribe to existing events without changing the producer.
- Hinders evolution: event schemas become contracts that are easy to break without noticing. Flows that span many processors are hard to understand, test, and change. Error handling and eventual consistency add complexity.
- Fitness functions: schema compatibility checks in the pipeline (for example, a schema registry that rejects breaking changes); every event type has a documented owner; end-to-end tracing across consumers.
- AWS note: EventBridge (with its schema registry), SNS/SQS, Kinesis, or Amazon MSK; Step Functions for mediator-style orchestration.
Microservices
Section titled “Microservices”Many small, independently deployable services, each owning its data and aligned to a bounded context.
- Helps evolution: the most independent quanta of any common style. Each service can have its own characteristics, technology, and release cadence, and can be replaced without touching the others. Thoughtworks compares swapping a service to swapping one Lego brick for another.
- Hinders evolution: operational cost (deployment, observability, networking, data consistency) is high. If services share a database or chain synchronous calls, you get a distributed monolith: all the cost and none of the independence.
- Fitness functions: no shared databases; limits on synchronous call depth; consumer-driven contract tests; each service independently deployable (deploy one without deploying others).
- AWS note: typically EKS or ECS per service, a data store per service, API Gateway or a service mesh, and a strong platform (golden paths) to keep the per-service cost down.
Serverless on AWS
Section titled “Serverless on AWS”Not a separate style in the catalogue, but a common way to implement service-based, event-driven, or microservice architectures on AWS: Lambda, API Gateway, EventBridge, Step Functions, DynamoDB.
- Helps evolution: very fine-grained deployment, little infrastructure to manage, and native event integration.
- Hinders evolution: heavy use of proprietary services is a form of the vendor king trade-off. Logic can scatter across many functions and event rules (sometimes called Lambda pinball), which makes change hard to reason about.
- Quanta: a quantum is usually a service (its functions, its table, its events), not each individual function. Design boundaries at that level.
- Fitness functions: every function belongs to an owning service (tags); no function writes to another service’s table (IAM checks); timeouts and concurrency limits are set.
Moving between styles
Section titled “Moving between styles”The style itself can evolve. A common path:
- Start as a modular monolith. Fowler’s MonolithFirst argues that most successful microservice systems started as monoliths that were later split, partly because good service boundaries are hard to see up front.
- Enforce module boundaries with fitness functions until they’ve proven stable.
- Extract modules that need different characteristics (scale, availability, release cadence) into services, using the strangler fig pattern.
- Split the data with expand and contract so each extracted service becomes a real quantum.
- Introduce events where synchronous calls are coupling quanta together.
Styles also mix: a microservices system can use event-driven communication, and an individual service can be a small layered application.
Sources
Section titled “Sources”- Neal Ford, Rebecca Parsons, Patrick Kua, Building Evolutionary Architectures, chapter 4, Architectural Coupling (1st ed.): quantum size by style; big ball of mud as the degenerate case.
- Mark Richards, Neal Ford, Fundamentals of Software Architecture (O’Reilly): the style catalogue, broker vs. mediator topologies, and per-style characteristic ratings.
- Thoughtworks, Microservices as an evolutionary architecture.
- Martin Fowler, MonolithFirst and MicroservicePremium.
- InfoQ, Martin Fowler on the microservices premium.