software engineering5 min read
Architecture styles: a working vocabulary

Architecture styles are less a set of blueprints than a shared vocabulary. When someone says "event-driven" or "CQRS" in a design review, everyone in the room should know what that choice gives you and what it will cost later. These are short definitions of seven common ones, each with the trade-offs I'd want a team to weigh.
Monolithic architecture
In a monolithic architecture, the user interface, business logic and data access layers are built into a single unit. That unit runs as one service and is usually built, deployed and scaled as one. It's straightforward to develop and deploy, but because the components are tightly coupled it's hard to separate them, which leads to scaling and maintenance problems as the system grows.
Pros
- Easy development: one codebase and one development environment.
- Simple, consistent data access: one codebase and usually one database, so data access is simple and consistent.
- Simple deployment: a single deployable unit keeps the deployment pipeline simple.
Cons
- Scaling: you scale by running more copies of the whole application, which uses more resources.
- Coupling: interdependent components mean one failure can bring down the whole system.
- Long builds: even a small change can mean rebuilding the whole application.
Layered (n-tier) architecture
A layered architecture splits an application into layers, typically presentation, business logic and data storage. Each layer talks to the layers next to it through well-defined interfaces. Strictly, layers are logical divisions in the code, while tiers are physical deployment boundaries, so an n-tier system runs its layers as separately deployed "tiers". The separation means changes, and in the n-tier case scaling, can be confined to one layer, at the cost of more interactions between components.
Pros
- Separation of concerns: each layer has its own responsibility.
- Flexibility: the modular design makes changes easier.
- Scalability: in an n-tier deployment, each tier can be scaled separately.
Cons
- Complexity: more layers and interactions add complexity.
- Performance: extra layers, and especially network hops between tiers, add latency.
- Maintenance: more moving parts need more upkeep.
Event-driven architecture
Event-driven architecture (EDA) uses events, meaning changes in state, to trigger actions between decoupled components. Components produce, consume or react to events, which lets them communicate asynchronously. It's useful for applications that need to respond quickly as things happen, but managing and tracing events is hard.
Pros
- Responsiveness: suits systems that must react as soon as something happens.
- Decoupling: makes later changes and extensions easier.
- Scalability: new components can be added that simply listen for events.
Cons
- Event sprawl: a high volume of events adds complexity.
- Debugging: event-based flows are hard to trace and debug.
- Consistency: keeping state consistent across services is hard.
Microkernel architecture
A microkernel architecture uses a small core system for the application's minimal functions. Everything else is built as plugins or external modules that interact with the core. It's very extensible and modular, though it can add latency and management complexity.
Pros
- Extensibility: new features can be added as plugins.
- Isolation: plugins can be isolated so that one failing plugin doesn't take down the system. This isn't automatic; plugins running in the same process aren't isolated by default.
- Reuse: modules can be reused in other projects or components.
Cons
- Complexity: interactions between plugins and the core can get complicated.
- Performance: extra layers and interfaces can add latency.
- Development time: building the core system and the plugins takes time.
Microservices architecture
In a microservices architecture, the application is split into a collection of loosely coupled services that can be deployed independently. Each specialised service focuses on a single business capability, and the services communicate through APIs. It offers a lot of scalability and flexibility, but it brings operational complexity and latency from the communication between services.
Pros
- Scalability: each service can be scaled independently.
- Flexibility: different services can use different technologies.
- Fault isolation: a failure in one service has limited impact on the others.
Cons
- Complexity: needs careful design and coordination.
- Latency: communication between services adds delay.
- Operational overhead: you need good tooling for orchestration and monitoring.
Serverless architecture
Serverless architecture takes server management off your hands, so developers can focus on functions that run in response to events such as HTTP requests or file changes. It scales well and can be cost-effective, especially for spiky or low traffic, though at steady high load it can cost more than running your own servers. The main challenges are the latency of the first invocation (cold starts) and limited control over the runtime environment.
Pros
- Cost: you pay for execution time, which can be cheap for spiky or low workloads.
- Automatic scaling: capacity follows the volume of incoming requests.
- Developer focus: no server maintenance.
Cons
- Cold starts: the first invocation of a function can be slow.
- Limited control: less control over server configuration.
- State management: functions are stateless, so state has to live somewhere else.
CQRS
Command Query Responsibility Segregation (CQRS) is a pattern rather than a whole-system architecture. It separates an application's reads and writes into different models, so each can be scaled and optimised independently. With separate read models, CQRS usually accepts eventual consistency by design: the challenge is making the lag between a write and the updated read model acceptable and visible to users, not achieving strong consistency. It's often paired with event sourcing, which stores every change as an event. Teams choose event sourcing for its audit history and the ability to rebuild state, not for performance.
Pros
- Scalability: reads and writes can be scaled independently.
- Performance: read and write models can each be optimised for their job.
- Complexity management: separating them can simplify complicated business logic.
Cons
- Eventual consistency: reads can lag behind writes, and the application has to handle that.
- Complexity: it adds another layer of development and operational work.
Key takeaways
- Monolithic: simple, but hard to scale.
- Layered (n-tier): separates concerns, but adds complexity.
- Event-driven: suits systems that must react quickly, but can be hard to manage.
- Microkernel: extensible, but can have performance costs.
- Microservices: scalable, but operationally complex.
- Serverless: can be cost-effective, but has limitations.
- CQRS: lets reads and writes scale separately, but adds complexity and eventual consistency.