System Architecture Styles
A system architecture maps the relationships between multiple applications or services: the data that flows between them and the interfaces they expose. It usually spans several teams or business units, so it’s heavily shaped by organisational structure, as Conway’s Law predicts.
Unlike a single application, a system rarely gets to choose one style. It evolves over years, often with a modernisation roadmap, so most real systems mix styles: a few services carved out around a core of older applications, events for some flows, a nightly batch for others. The useful question isn’t which style is right, but which fits each part of the system.
It’s also worth noticing how the industry swings between extremes before settling somewhere in the middle. SOA gave way to microservices. SOAP and XML gave way to REST and JSON, and then JSON Schema and OpenAPI brought back much of the contract that SOAP had provided. Each swing corrects a real problem and then overshoots.
Microservices Architecture
Section titled “Microservices Architecture”Microservices decompose a system into small, independently deployable services, each owning its own data and delivering a specific business capability. They work well at scale, where many teams need to build and release independently of each other. That team autonomy is the real benefit, and it’s why I favour a polyglot approach over rigid enterprise standards.
The problem is that microservices are often adopted too early. Without the team structure or scale to justify them, the result is usually a distributed monolith: services that have to be deployed together, chatty calls across the network, and sometimes a shared database underneath. You pay the operational cost of a distributed system without getting the independence. That’s why my default is a modular monolith, extracting services only when a specific team or scaling boundary demands it, much as Martin Fowler argues in “Monolith First”. Logical decoupling gives you most of the benefit; physical decoupling is a cost to justify (see Key Concepts).
When services are the right answer, their boundaries should follow bounded contexts and team ownership rather than technical layers.
Service-Oriented Architecture (SOA)
Section titled “Service-Oriented Architecture (SOA)”SOA integrates large, coarse-grained business applications by exposing their functions as reusable services, typically connected and orchestrated through a centralised Enterprise Service Bus (ESB). My own integration background is in BizTalk, so this is a style I’ve worked with directly.
It’s easy to treat SOA as legacy, and in many ways it is. The ESB tended to become a “smart pipe”: business logic accumulated in the middleware, owned by a central team, and the bus became a bottleneck for every change. The microservices principle of “smart endpoints and dumb pipes” was a direct reaction to that.
But I think SOA is also unfairly maligned. The core ideas were sound: services aligned to business capabilities, explicit contracts between them, and designing for reuse. Much of what went wrong came from heavyweight vendor tooling and central governance rather than from service orientation itself. Many enterprises still run SOA estates, and they’ll be around for a long time, so it’s a style worth understanding rather than dismissing.
Event-driven Architecture
Section titled “Event-driven Architecture”In an event-driven architecture, decoupled components communicate asynchronously by publishing and consuming events through a broker, so systems can react to changes without calling each other directly. It applies at both levels: across applications and teams at the system level, or between modules within a single application.
I use it where coupling is the problem, not as a default (see Ways of Working). Asynchronous systems are harder to debug and reason about, and a rule that everything must be event-based adds complexity before it’s needed. The three questions I ask when choosing between synchronous and asynchronous communication are set out in Key Concepts. The first is the one that matters most here: if the trigger is a genuine business event, publishing it reduces coupling and makes the system easier to adapt when the business process changes.
REST and API-First
Section titled “REST and API-First”REST comes from Roy Fielding’s 2000 dissertation, which defines it as a set of constraints for network-based systems: client-server, statelessness, cacheability, a uniform interface, a layered system, and optionally code on demand. In practice “REST” usually means something looser: resource-oriented JSON over HTTP, rarely with every one of Fielding’s constraints applied. Hypermedia (HATEOAS), part of the uniform interface, is the one almost everyone skips. That’s fine, as long as the design is consistent.
What matters more to me is the contract. I work API-first: define the contract before writing the implementation, so consumers and providers can work in parallel and the interface is a deliberate design decision rather than a by-product of the code. This is the swing described at the top of the page: SOAP had formal contracts through WSDL, early REST dropped them, and OpenAPI brought them back. The middle ground is where I’d land: a lightweight transport with an explicit, versioned contract.
Batch Architecture
Section titled “Batch Architecture”Batch architecture processes data in scheduled or triggered bulk runs rather than continuously as it arrives. It trades latency for throughput and simpler coordination between systems (see latency vs. throughput).
It still has a clear place. Reporting, ETL, analytics loads and file-based partner integrations rarely need to be real-time, and making them real-time adds cost and complexity for no benefit the user actually feels. Not everything needs to be an event stream.
Shared-data Architecture
Section titled “Shared-data Architecture”In a shared-data architecture, applications integrate implicitly by reading from and writing to the same database. Between services, I treat this as an anti-pattern. The schema becomes an unversioned interface that nobody owns, so a change made for one application can silently break another, and the coupling is hidden from anyone reading the code.
The alternative is for each application or service to own its data within its own bounded context, and expose it through an explicit interface (see Architecture Levels and Terminology). The exception is analytics: data warehouses and data lakes bring data from many systems together for reporting, and read-only sharing of that kind is a different matter from transactional systems writing to each other’s tables.
References
Section titled “References”- Melvin Conway — “How Do Committees Invent?”, Datamation (1968).
- Roy Fielding — “Architectural Styles and the Design of Network-based Software Architectures”, PhD dissertation, University of California, Irvine (2000).
- Martin Fowler — “Monolith First” (2015).
- James Lewis and Martin Fowler — “Microservices” (2014).
