Be Software Architecture Wise
Be Wise Apps

Leer en español · Lire en français · Auf Deutsch lesen

Section 5.1 of 5.8 · Intermediate · 22 min read

Architectural styles, patterns and tactics

Video lesson

5 min · narrated, with captions
Listen only (audio)
Read the transcript

In this lesson you'll learn the vocabulary that Chapters five and six rely on: what a style is, how it differs from a pattern and a tactic, and how to count the truly independent parts of a system.

Think of house types: a terrace, a bungalow, a tower block. Each name tells a builder a lot before any drawing exists: how many floors, how the rooms connect, what it costs, and where it works well. Software has its own named shapes, called architectural styles: a layered application, a set of modules in one program, a chain of processing steps, a group of separate services. Knowing them, and what each is good and bad at, lets a team pick a sensible shape quickly and describe it in a few words.

David Garlan and Mary Shaw described an architectural style as a vocabulary of component and connector types, together with constraints on how they can be combined. Pipes and filters, for example, forbids filters from sharing state or knowing their neighbours. Follow that rule, and you get filters that can be tested, reused and rearranged on their own. That's not luck; it's a consequence of the rule. So a style is not a picture to copy. It's a set of rules that buy specific qualities, at a price. Adopt the vocabulary without the rules, and you pay the price without the benefits.

Four related terms are often used loosely. A style describes the overall shape: which parts and connections are allowed, and under what rules. A pattern is a proven solution to a recurring structural problem in a stated context, such as Broker or Model View Controller. A tactic is smaller: one design decision that moves one quality attribute, such as a cache to cut latency. Patterns usually package several tactics. And a reference architecture is a template for a whole domain, such as AUTOSAR for the electronics in cars. Experts disagree about where style ends and pattern begins; spend your energy on the constraints instead.

The first big decision is how to divide the system at the top level. Technical partitioning groups code by technical role: presentation, business logic and persistence. Suppose a food-delivery company lets customers tip the courier. That needs a new field on a screen, a new rule and a new database column. One change touches every layer. Domain partitioning groups code by business capability instead: orders, payment, delivery and menus, each with its own interface, logic and data. Here, the same change lands almost entirely in the delivery module. Most business changes are phrased in domain terms, so domain partitioning tends to keep them in one place. Technical partitioning is simpler, and suits small systems built by one team.

The second distinction is deployment. A monolith is deployed as one unit. A distributed system is several units cooperating over a network, with all the network's costs. A sharper idea is the architecture quantum. A quantum is a part that can be deployed independently, does one coherent business job, and includes everything it needs, its database in particular. A monolith is, by definition, one quantum. Three services that share one database are still one quantum. None can change the schema or be restored on its own, so they rise and fall together. Only when each service owns its data, and they exchange events, do you get three quanta. This matters because architecture characteristics apply per quantum. Parts with very different needs must be separate quanta.

Let's classify four designs for the food-delivery platform. Design one is a single application with folders for views, services and models. Technical partitioning, one unit, one quantum. Every feature crosses all three folders. Design two has packages for orders, payments, delivery and menus, each with its own tables. Still one quantum, but changes stay local, and the modules could later be separated. Design three makes them four services sharing the same tables. Four deployment units, but one quantum: a schema change or a database outage hits all four. All the costs of distribution, few of its benefits. Design four gives each service its own database. Now there are four quanta, each released on its own, at the price of network failures and eventual consistency.

Carry three points forward. Monolith describes deployment, not quality. Split a big ball of mud into services and you get a distributed ball of mud, which is worse. Real systems are hybrids. A modular monolith may be layered inside each module, and run a nightly pipes and filters job. And a style is not a goal in itself. Start from your architecturally significant requirements, pick the simplest style that meets them, record the choice, and enforce its rules.

The full section has a table of every style in Chapters five and six, and more on quanta. Next, in Section five point two, we look at the most familiar style of all: layered architecture. See you there.

What you need to know

Why architects name shapes

Most systems are not designed from nothing. Experienced architects recognise that a new problem resembles one they have seen before and reach for a shape that is known to work: a few layers for a straightforward business application, a chain of processing steps for a data feed, a core with plug-ins for a product that customers extend. Naming those shapes gives a team a shared vocabulary. Saying it is a modular monolith with event-based communication between modules conveys in ten words what would otherwise take a page of diagrams, and it brings with it a body of knowledge about what tends to go right and wrong.

David Garlan and Mary Shaw put this on a firm footing in An Introduction to Software Architecture (Carnegie Mellon technical report, 1994) and in their book Software Architecture: Perspectives on an Emerging Discipline (1996). They described an architectural style as a vocabulary of component and connector types together with a set of constraints on how they can be combined. Pipes and filters, for example, has one kind of component (a filter that transforms data) and one kind of connector (a pipe that carries it one way), and it constrains filters not to share state or know who their neighbours are. Roy Fielding, whose dissertation (2000) defined REST as a style, made the same point more sharply: a style is a coordinated set of constraints, and it is the constraints that produce the properties. Forbid filters from sharing state and you get filters that can be tested, reused and rearranged independently; that is not luck, it is a consequence of the rule.

This is the most useful way to read every style in this chapter and the next. A style is not a picture to copy. It is a set of rules you agree to follow, chosen because following them buys specific architecture characteristics, the quality attributes of Chapter 2, at a price. A team that adopts the vocabulary of a style but not its rules gets the price without the benefits.

Styles, patterns, tactics and reference architectures

Four related terms are used, often loosely, for reusable design knowledge. They differ mainly in scope and in how much they prescribe.

An architectural pattern describes a proven solution to a recurring structural problem in a stated context. The idea of patterns came from the building architect Christopher Alexander and entered software through the design patterns of Gamma, Helm, Johnson and Vlissides (1994), which work at the scale of a few classes. Pattern-Oriented Software Architecture, Volume 1: A System of Patterns by Frank Buschmann, Regine Meunier, Hans Rohnert, Peter Sommerlad and Michael Stal (1996), usually called POSA, catalogued patterns at the scale of whole systems: Layers, Pipes and Filters and Blackboard (grouped as getting from mud to structure), Broker for distributed systems, Model-View-Controller and Presentation-Abstraction-Control for interactive systems, and Microkernel and Reflection for adaptable systems. Each POSA pattern is written as context, problem, forces and solution, with consequences, which makes the trade-offs explicit.

A tactic, in the sense used by Len Bass, Paul Clements and Rick Kazman in Software Architecture in Practice, is smaller: one design decision that influences one quality attribute response, such as adding a heartbeat to detect a failed server or a cache to cut latency. Section 2.6 used modifiability tactics such as encapsulation and using an intermediary. Patterns package several tactics: the Layers pattern combines encapsulation, restricting dependencies and using an intermediary.

A reference architecture is a template for a whole domain or platform: it names the typical building blocks and how they connect, so that projects adapt it rather than start from a blank page. AUTOSAR, the layered software architecture used for electronic control units in cars, is a well-known example; the architecture centres published by cloud providers offer many smaller ones. A reference architecture is usually built from several styles and patterns at once.

Practitioners do not agree on where the line between style and pattern falls. POSA calls Layers a pattern; Garlan and Shaw call layered systems a style; Mark Richards and Neal Ford, in Fundamentals of Software Architecture, reserve style for the overall topology of a system and pattern for lower-level, reusable structures that can appear inside many styles. This course follows the common usage: style for the overall shape of a system or of a large part of it, pattern for a reusable solution that can sit at any level. Do not spend energy on the boundary; spend it on the constraints and their consequences.

Four kinds of reusable design knowledge

TermScopeExampleQuestion it answers
Architectural styleWhole system or a large partLayered, pipes and filters, microservicesWhat kinds of parts and connections are allowed, and under what rules?
Architectural patternAny structural levelBroker, Model-View-Controller, MicrokernelWhat is a proven solution to this recurring structural problem, and what does it cost?
TacticOne design decisionHeartbeat, cache, encapsulation, retry with back-offWhich single decision moves this quality attribute in the direction we need?
Reference architectureA domain or platformAUTOSAR for car electronics; cloud providers' published architecturesWhat do systems of this kind usually contain, and how is it usually arranged?

Two ways to cut a system: technical or domain partitioning

The first structural decision in any style is how to divide the system at the top level. There are two basic answers. Technical partitioning groups code by technical role: everything to do with the user interface in one place, all business rules in another, all database access in a third. Domain partitioning groups code by business capability: everything to do with orders in one place, payments in another, delivery in a third, each containing whatever user interface, logic and data access it needs.

The difference shows up the moment a feature changes. Suppose a food-delivery company lets customers add a tip for the courier. In a technically partitioned system that change needs a new field on a screen (presentation), a new rule for tip limits and payouts (business logic) and a new column and query (persistence): three parts, often owned by three different people or teams. In a domain-partitioned system the same change lands almost entirely in the delivery module. Most business change requests are phrased in domain terms, so domain partitioning tends to keep a change in one place. Technical partitioning keeps a technical change in one place, such as replacing the database access library, but those changes are rarer.

The choice also shapes the organisation. By Conway's law, a technically partitioned system tends to be built by a front-end team, a back-end team and a database team, and every feature needs a hand-off between them. A domain-partitioned system can be built by teams that each own a business capability end to end. Martin Fowler made the same recommendation in 2015: presentation, domain and data layers are a good way to organise a small piece of software, but a larger system's top level should be split into domain-oriented modules, each layered inside if that helps.

Neither is wrong. Technical partitioning is simple, familiar and supported by almost every web framework's default folder structure, and it suits small systems where one team does everything. Domain partitioning costs more thought up front, because someone has to find good domain boundaries (the subject of Section 5.7), but it scales better with the size of the system and the number of teams.

The same feature under two partitionings

The same food-delivery application partitioned two ways: by technical layer, where a feature crosses every layer, and by domain, where a feature stays inside one moduleTechnical partitioningDomain partitioningPresentationscreens, controllersBusiness logicservices, rulesPersistencerepositories, SQLOne change, courier tips,touches every layerOrdersUIlogicdataPaymentUIlogicdataDeliveryUIlogicdataMenusUIlogicdataThe same change landsin one module
With technical partitioning a typical feature crosses every layer; with domain partitioning it lands in one module, which may still be layered inside.

Monolithic and distributed styles

The second top-level distinction is about deployment. A deployment unit is the thing that is built, released and started as one piece: a single executable, a container image, a web archive. In a monolithic architecture the whole application is one deployment unit, usually with one database. It can run as many identical copies behind a load balancer, but every copy contains all the code, and releasing any change means releasing all of it. In a distributed architecture the system is several deployment units that cooperate by sending messages over a network, and each can be released and scaled separately.

Two clarifications avoid much confusion. First, monolith describes deployment, not quality. A monolith can be beautifully modular or a big ball of mud (Brian Foote and Joseph Yoder's name for a system with no discernible structure), and so can a distributed system: many teams have learned that splitting a ball of mud into services gives a distributed ball of mud, which is worse. Second, being distributed is not a free upgrade. Every call that crosses a network can be slow, fail halfway or arrive twice, data can no longer be changed in one local transaction, and the system needs monitoring, deployment automation and on-call skills that a monolith can manage without. Chapter 6 covers those costs in detail. This chapter covers the styles that avoid them: everything runs in one deployment unit, and the architecture lives in how the code inside it is organised.

The architecture quantum

Monolithic and distributed is a useful first cut but too coarse for real decisions. Neal Ford, Rebecca Parsons and Patrick Kua introduced a sharper idea in Building Evolutionary Architectures (2017), refined by Ford, Richards, Sadalage and Dehghani in Software Architecture: The Hard Parts (2021): the architecture quantum. A quantum is a part of the system that can be deployed independently, has high functional cohesion (it does one coherent job for the business) and includes everything it needs to work, its database in particular. The later definition adds that the parts of a quantum are statically coupled (they share code, schemas or infrastructure that must be deployed together) and that synchronous calls bind quanta together operationally for as long as the call lasts.

Why does this matter? Because architecture characteristics apply per quantum. A quantum is scaled, deployed, secured and recovered as a whole, so it has one level of scalability, one level of availability, one release cadence. If two parts of a system need very different characteristics, for example a courier-location feed that must absorb thousands of updates per second and a monthly restaurant-invoicing job, they can only get them by being in different quanta. Conversely, splitting code into several services does not create several quanta if they still share one database: none of them can change the schema or be restored on its own, so they rise and fall together. A monolith is, by definition, one quantum.

Counting quanta

Three architectures and their quanta: a monolith with its database is one quantum; three services sharing one database are still one quantum; three services each owning their data and exchanging events asynchronously are three quantaA: monolithApplicationone deploymentDatabase1 quantumB: services, shared databaseABCShared database1 quantumC: own data, eventsADBBDBCDB3 quantaDashed boundaries mark each quantum: what can be deployed and run on its own, data included
A shared database binds services into one quantum; separate quanta need separate deployment and separate data.

Worked example

Worked example: classifying four designs

A food-delivery company is reviewing four candidate designs for its platform. For each, the architect asks three questions: how is the top level partitioned, how many deployment units are there, and how many quanta?

Design 1: one Django application with folders views, services and models, one PostgreSQL databaseTechnical partitioning; one deployment unit; one quantum. Simple, but every feature crosses all three folders.
Design 2: one application with packages orders, payments, delivery and menus, each with its own tables, one database serverDomain partitioning; one deployment unit; one quantum. Changes stay local, and the modules could later be separated.
Design 3: four services (orders, payments, delivery, menus) that all read and write the same database tablesDomain partitioning in name; four deployment units but one quantum, because a schema change or a database outage affects all four. All the costs of distribution, few of its benefits.
Design 4: the same four services, each with its own database, exchanging events through a message brokerDomain partitioning; four deployment units; four quanta. Each can be scaled and released independently, at the price of network failures and eventual consistency between them.

Designs 1 and 2 cost the same to run, but design 2 makes typical changes cheaper and keeps future options open.

Design 3 is the warning case: the number of deployment units says little; the number of quanta, which depends on data and coupling, says what can really evolve independently.

How to read the style sections that follow

Sections 5.2 to 5.5 and most of Chapter 6 describe one style each, always in the same order, so that styles can be compared: a definition and where the style came from; a diagram; how it works; a concrete example system; a table rating its main characteristics; when to use it and when not to; common anti-patterns; and how to enforce it in code, because a style that is not checked erodes, as Section 1.7 described.

The characteristic ratings (Low, Medium, High, each with a reason) follow the approach of Richards and Ford's style comparisons, but they are this course's own judgements. Read them as relative and typical: they compare styles as they are usually built, by a competent team, for a typical business system. A disciplined team can make a layered monolith that deploys many times a day, and a careless one can build microservices that cannot be deployed at all. The ratings tell you where the style helps and where you will have to work against it.

Two sections in this chapter are not deployment styles. Hexagonal, onion and clean architecture (Section 5.6) describe how to organise the inside of an application or service so that business logic does not depend on technology. Domain-driven design (Section 5.7) is a way of finding the domain boundaries that modular monoliths and microservices rely on. Both apply equally inside a monolith or a distributed system.

The styles in Chapters 5 and 6 at a glance

StyleMain partitioningDeploymentTypical fit
Layered (5.2)TechnicalMonolithicSmall to medium business applications built by one team
Modular monolith (5.3)DomainMonolithicGrowing products with several teams that want one deployment
Pipes and filters (5.4)Technical (processing steps)Monolithic or distributedData processing: ETL, media, logs, compilers
Microkernel (5.5)Technical core, plug-ins often by domainUsually monolithicProducts extended by customers or third parties; rules engines
Hexagonal, onion, clean (5.6)Inner structure: domain at the centreEitherAny application whose logic must outlive its frameworks
Service-based (6.2)DomainDistributed, few coarse servicesA pragmatic step beyond a monolith, often sharing a database
Service-oriented architecture (6.2)Technical (orchestration, enterprise services)DistributedEnterprise integration, now largely historical
Microservices (6.3)DomainDistributed, many fine-grained servicesMany teams that must release independently at scale
Event-driven (6.4)Technical (event processing)DistributedHighly responsive, decoupled reactions to business events
Serverless and space-based (6.8)VariesDistributedSpiky or extreme load; pay-per-use workloads

Styles combine, and they are not goals

Real systems are hybrids. A modular monolith may be layered inside each module; one of its modules may run a nightly pipes-and-filters job; a microservice may be built in hexagonal style; an event-driven system may use a microkernel to let customers add their own rules. Naming the style of each part is still useful, because each part then inherits a known set of rules and trade-offs.

The common mistake is to treat a style as an aim in itself: we are moving to microservices as a strategy, rather than as a means to a stated quality such as independent deployment for twelve teams. Start from the architecturally significant requirements of Chapter 2, pick the simplest style whose characteristics meet them, write the choice down as a decision record and enforce its rules. Section 5.8 gives a decision guide for the styles in this chapter.

Check your understanding

According to Garlan and Shaw, what two things define an architectural style?

A vocabulary of component and connector types, and a set of constraints on how they may be combined. The constraints are what produce the style's characteristic qualities.

What is the difference between a tactic and a pattern?

A tactic is one design decision aimed at one quality attribute response, such as a heartbeat or a cache. A pattern is a larger, documented solution that typically combines several tactics.

Why might four services that share one database count as a single architecture quantum?

Because none of them can change the shared schema, be restored or be scaled at the data level without affecting the others; they are deployed and fail together in practice, so they share one set of operational characteristics.

Which kind of change does technical partitioning keep in one place?

A technical change, such as replacing the database access library or the user-interface framework. Typical business changes, by contrast, cut across all the technical layers.

Next · 5.2LayeredThe most familiar style: presentation, business, persistence and database layers. Closed and open layers, layers of isolation, the sinkhole anti-pattern, why every feature touches every layer, and how to enforce the rules.Continue →

Sources and further reading

Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.

Ask: “Our four services all use the same PostgreSQL database. Is that really a problem, or just a theoretical one?”