Software architecture shapes a project long before the first line of code. Choosing the wrong one is expensive later; knowing the options makes the choice deliberate.
Here are six common architectures, with what each one gives you and what it costs.
1. Monolithic architecture
The traditional model: interface, business logic and data access live in one deployable unit.
- Pros: simple to develop, test and deploy; in-process calls are fast.
- Cons: hard to scale individual parts; maintenance grows with the codebase.
2. Microservice architecture
The application is split into small independent services that talk over APIs. Each service owns a function and can be developed, deployed and scaled on its own.
- Pros: independent scaling, technology flexibility, fault isolation.
- Cons: distributed complexity, data consistency, network overhead.
3. Service-oriented architecture (SOA)
Similar to microservices, but with larger, coarser services that communicate through a message bus or broker and are reused across applications.
- Pros: reuse across projects, standardized integration.
- Cons: needs solid infrastructure; the bus adds latency.
4. Event-driven architecture
Components produce, detect, consume and react to events, which makes asynchronous processing natural.
- Pros: scales by adding handlers; loose coupling.
- Cons: event-flow complexity, harder debugging and consistency.
5. Layered architecture
The application is organized in layers - presentation, business logic, data access - each responsible for its own job.
- Pros: clear separation of concerns, layers evolve independently.
- Cons: overhead between layers; changes can ripple through several of them.
6. Microkernel architecture
A small core provides minimal functionality, and everything else arrives as plugins or extensions.
- Pros: extensibility without touching the core; a small, manageable centre.
- Cons: plugin dependency management; overhead from the plugin layer.
A practical decision path
Start with a modular monolith. One deployable, explicit module boundaries, a separate schema per module, no cross-module queries through the back door. Most products live and grow on this for years, and the boundaries make a later split cheap.
Extract a service when a module has its own scaling profile, its own release cadence, or its own failure domain. A PDF generator that eats CPU on every upload is a candidate. A login form is not.
Choose event-driven when work can happen after the response: email, exports, billing, search indexing. Do not make events the default transport for request and response; you will spend the next year debugging a flow you cannot replay.
Use SOA when several products already exist and have to share a bus. Use microkernel when the core is stable and the variation belongs in extensions, as in a CMS, an IDE or a browser.
What the choice costs after launch
- Operational load. Every deployable needs logs, metrics, tracing, alerts and a rollback plan. Five services mean five of each.
- Data consistency. A transaction across services becomes a saga or an outbox table, and someone has to own the failure path.
- Team shape. One service per team works. One service per developer turns every release into a coordination meeting.
- Hiring and on-call. Distributed systems need people who can operate them at 3 a.m., not only design them on a whiteboard.
Migration is normal
Architecture is not a one-time decision. A modular monolith that outgrows its database splits a read-heavy module first. A microservice estate that drowns in coordination merges services back. The useful question is not which pattern is modern, but which change is cheapest next year.
How to choose a software architecture
The microservices pattern is described in detail by Martin Fowler. There is no best architecture - only a fit for your requirements. Ask three questions: how much scale do you need, how fast will the team and the product change, and how much operational complexity can you afford?
A monolith is a perfectly good answer for a small product with an uncertain future. Microservices make sense when independent scaling and team autonomy outweigh the operational price. Event-driven pays off when asynchronous flows are the core of the business. The mistake is not choosing a simple architecture - it is choosing a fashionable one you cannot operate.
Understand the trade-offs, start with the simplest thing that works, and let the architecture evolve with the product.
Related reading: The importance of correct naming in code and SQL depth: one query instead of a backend service.