|
3 | 3 | - [Architect for flow](#architect-for-flow) |
4 | 4 | - [Context](#context) |
5 | 5 | - [The pattern](#the-pattern) |
| 6 | + - [The mindset](#the-mindset) |
| 7 | + - [Core concepts and how they fit together](#core-concepts-and-how-they-fit-together) |
| 8 | + - [Independent value streams (Lean Thinking)](#independent-value-streams-lean-thinking) |
| 9 | + - [Stream-aligned teams (Team Topologies)](#stream-aligned-teams-team-topologies) |
| 10 | + - [Services, products, and ownership](#services-products-and-ownership) |
| 11 | + - [Bounded contexts and domain alignment](#bounded-contexts-and-domain-alignment) |
| 12 | + - [Fast flow through small batches](#fast-flow-through-small-batches) |
| 13 | + - [Co-creation, codesign, and coevolution](#co-creation-codesign-and-coevolution) |
6 | 14 | - [Benefits](#benefits) |
7 | | - - [Caveats](#caveats) |
8 | | - - [Details](#details) |
| 15 | + - [Practical guidance](#practical-guidance) |
9 | 16 | - [Examples](#examples) |
10 | 17 |
|
11 | 18 | ## Context |
12 | 19 |
|
13 | | -- These notes are part of a broader set of [principles](../principles.md) |
14 | | -- This pattern is closely related to the [deliver little and often](little-and-often.md) pattern |
15 | | -- See also: [structured code](../practices/structured-code.md) |
| 20 | +- This pattern is part of a broader set of [engineering principles](../principles.md). |
| 21 | +- It strongly complements the [deliver little and often](./little-and-often.md) pattern. |
| 22 | +- It aligns with modern thinking on architecture modernisation, [evolutionary design / domain-driven design (DDD)](https://martinfowler.com/tags/evolutionary%20design.html), [Team Topologies](https://teamtopologies.com/key-concepts), and [Better Value Sooner Safer Happier (BVSSH)](https://www.soonersaferhappier.com/post/what-is-bvssh). |
| 23 | +- It should be read alongside [structured code](../practices/structured-code.md). |
| 24 | + |
| 25 | +At its core, _architecting for flow_ is about designing systems and teams so that valuable change can move quickly, safely, and sustainably from idea to production. |
16 | 26 |
|
17 | 27 | ## The pattern |
18 | 28 |
|
19 | | -Technical design choices should primarily optimise for rapid, reliable delivery and operations. |
| 29 | +Design architecture to enable fast, safe flow of change across independent value streams. |
20 | 30 |
|
21 | | -One key consideration is how best to break the system down into independent services/components. |
| 31 | +Architecting for flow means intentionally shaping systems around: |
22 | 32 |
|
23 | | -## Benefits |
| 33 | +- Independent value streams |
| 34 | +- Stream-aligned teams |
| 35 | +- Clear bounded contexts |
| 36 | +- Loosely coupled services and products |
| 37 | + |
| 38 | +The primary goal is not technology elegance, reuse, or theoretical purity. The goal is fast flow, which is the ability for teams to deliver small, incremental changes frequently, with confidence. |
| 39 | + |
| 40 | +This requires aligning business domains, team ownership, services/products, and architecture boundaries so that: |
| 41 | + |
| 42 | +- teams can work independently |
| 43 | +- changes are small and low-risk |
| 44 | +- coordination and hand-offs are minimised |
| 45 | +- learning happens early and often |
| 46 | + |
| 47 | +## The mindset |
| 48 | + |
| 49 | +Modern architecture recognises that software systems do not exist in isolation. Their behaviour, quality, speed of change, and reliability are shaped just as much by people, team structures, incentives, and ways of working as by code, infrastructure, or technologies. |
| 50 | + |
| 51 | +Poorly shaped architectures create: |
| 52 | + |
| 53 | +- Change coupling across teams |
| 54 | +- Slow delivery and high coordination costs |
| 55 | +- Fragile systems and risky releases |
| 56 | +- Unhappy, frustrated engineers and users |
| 57 | + |
| 58 | +Well-shaped architectures enable: |
| 59 | + |
| 60 | +- Independent delivery |
| 61 | +- Faster learning |
| 62 | +- Better quality |
| 63 | +- Safer change |
| 64 | +- Happier teams and users |
| 65 | + |
| 66 | +Architecting for flow is therefore a business optimisation strategy, not just an engineering one. |
| 67 | + |
| 68 | +## Core concepts and how they fit together |
| 69 | + |
| 70 | +### Independent value streams (Lean Thinking) |
| 71 | + |
| 72 | +An **independent value stream** is the fundamental building block. Each value stream: |
| 73 | + |
| 74 | +- Is aligned to a business subdomain |
| 75 | +- Is owned by a single stream-aligned team |
| 76 | +- Delivers business outcomes, not just features |
| 77 | +- Can be developed and deployed independently |
| 78 | + |
| 79 | +Architecture should make independence real, not just aspirational. |
| 80 | + |
| 81 | +### Stream-aligned teams (Team Topologies) |
| 82 | + |
| 83 | +Each independent value stream is owned by a **stream-aligned team**. That team: |
| 84 | + |
| 85 | +- Owns the product or service end-to-end |
| 86 | +- Makes day-to-day product and technical decisions |
| 87 | +- Builds, deploys, operates, and improves what they own |
| 88 | +- Optimises for outcomes and flow, not hand-offs |
| 89 | + |
| 90 | +Architecture should reduce the need for teams to coordinate in order to deliver change. |
24 | 91 |
|
25 | | -In high level terms, systems which are designed to maximise rapid, reliable delivery and operations: |
| 92 | +### Services, products, and ownership |
26 | 93 |
|
27 | | -- Are cost efficient: teams don't waste their time fighting the tools or working with difficult architectures |
28 | | -- Improve business agility: these systems allow teams to respond more quickly to changes |
29 | | -- Improve reliability: these systems are easier to understand, which leads to fewer failures and shorter recovery times |
30 | | -- Improve team happiness: engineers are happy when the tools they work with let them get on with what they do best |
| 94 | +Architecting for flow means aligning: |
31 | 95 |
|
32 | | -In many cases, building a system as a set of independently running services/components has benefits: |
| 96 | +- Products and services |
| 97 | +- Codebases and repositories |
| 98 | +- Runtime boundaries |
| 99 | +- Team ownership |
33 | 100 |
|
34 | | -- Multiple components enable parallel development work by multiple teams |
35 | | -- Teams can work at their own cadence |
36 | | -- Changes with each component are easier to reason about and test |
37 | | -- The best tools can be chosen for each job, rather than being hampered by a common set of technologies which need to be adequate for all parts of the system but may only suit some parts of the system well |
38 | | -- Concerns such as scaling, resilience, etc, can be tailored on a per-component basis — for example avoiding the waste generated by scaling a monolith for the benefit of scaling one small aspect of the system |
39 | | -- It is possible to isolate the impact of catastrophic failure of any individual component |
40 | | -- Self-contained components with clear boundaries of responsibility reduce hand-offs between teams |
41 | | -- Components with clear boundaries of responsibility are more easily replaced |
42 | | -- Components with clear boundaries of responsibility more likely to be reusable — note: this does not promote building "generic" components — rather, components that have a clear scope |
| 101 | +A common smell is when: |
| 102 | + |
| 103 | +- Multiple teams must change their services together |
| 104 | +- Features require cross-team coordination by default |
| 105 | +- Teams share databases or internal implementation details |
| 106 | + |
| 107 | +Instead: |
| 108 | + |
| 109 | +- Each service or product should map clearly to a value stream |
| 110 | +- Ownership boundaries should be obvious |
| 111 | +- Dependencies should be explicit and intentional |
| 112 | + |
| 113 | +### Bounded contexts and domain alignment |
| 114 | + |
| 115 | +Clean **bounded contexts** are essential to flow. Key principles: |
| 116 | + |
| 117 | +- Split systems vertically by domain, not horizontally by technical layers |
| 118 | +- Avoid shared databases across bounded contexts |
| 119 | +- Encapsulate data, logic, and behaviour within each context |
| 120 | +- Accept duplication where it reduces coupling and coordination |
| 121 | + |
| 122 | +Bounded contexts reduce change coupling, allowing teams to move independently. Collaborative techniques such as [EventStorming](https://www.eventstorming.com/) are strongly encouraged to identify meaningful domain boundaries. |
| 123 | + |
| 124 | +### Fast flow through small batches |
| 125 | + |
| 126 | +Flow improves when teams: |
| 127 | + |
| 128 | +- Deliver small slices of value |
| 129 | +- Deploy frequently |
| 130 | +- Learn quickly from real usage |
| 131 | +- Reduce the blast radius of change |
| 132 | + |
| 133 | +Architecture should actively support: |
| 134 | + |
| 135 | +- Independent deployments |
| 136 | +- Progressive delivery |
| 137 | +- Feature toggling |
| 138 | +- Observability and fast feedback |
| 139 | + |
| 140 | +Large, coordinated releases are a sign that flow is being constrained by architecture. |
| 141 | + |
| 142 | +## Co-creation, codesign, and coevolution |
| 143 | + |
| 144 | +Architecting for flow is not a one-off design activity. It requires a **co-creation approach**, where: |
| 145 | + |
| 146 | +- Engineers, product managers, domain experts, and users collaborate |
| 147 | +- Architecture evolves incrementally |
| 148 | +- Decisions are revisited as learning increases |
| 149 | + |
| 150 | +Architecture should be **codesigned and coevolved** with the system: |
| 151 | + |
| 152 | +- Changes are made in small, safe steps |
| 153 | +- Learning feeds back into design |
| 154 | +- Teams continuously improve both the product and the architecture |
| 155 | + |
| 156 | +This is a deliberate move away from big up-front designs. |
| 157 | + |
| 158 | +## Benefits |
43 | 159 |
|
44 | | -## Caveats |
| 160 | +Systems architected for flow tend to deliver strong outcomes. However |
45 | 161 |
|
46 | | -- This pattern must not compromise quality: automation (including of quality control) is essential for safe implementation of this pattern |
47 | | -- Architectures with multiple moving parts are more complicated. While splitting a system into multiple components is often a good idea, "too many" components can cause more harm than good. There is usually a sweet spot for how many components to break a system down into — and for small or simple systems a monolith might be better. In distributed systems: |
48 | | - - There are more failure modes to test, since calls which go over the network can fail in more ways than simple method invocations |
49 | | - - Versioning becomes a more complicated concern, and additional effort is required to ensure component APIs are compatible as each changes independently |
50 | | - - Clean domain boundaries are essential for safe implementation of this pattern |
51 | | - - Comprehensive monitoring and alerting is essential for safe implementation of this pattern |
52 | | -- Components should be built because working in that way gives benefits, not purely because the components might be reused later: if they are later reused, that's even better |
| 162 | +- Flow must not compromise quality, strong automation is essential (testing, security, deployment) |
| 163 | +- More components ≠ better as over-fragmentation increases cognitive load and operational cost, small or simple systems may benefit from a well-structured modular monolith |
| 164 | +- Distributed systems introduce complexity, network failures has to be factored in, versioning challenges become a norm, observability becomes mandatory |
| 165 | +- Reuse is not the primary goal, build components for clear ownership and flow, reuse is a secondary benefit, not a justification |
53 | 166 |
|
54 | | -## Details |
| 167 | +There is usually a sweet spot between monoliths and over-distributed systems. |
55 | 168 |
|
56 | | -- Split services vertically via [bounded contexts](https://martinfowler.com/bliki/BoundedContext.html) rather than horizontally via technology layers: for example, do not implement dedicated processes to update databases or configuration |
57 | | -- For components to be genuinely independent they need to only interact via their public APIs (e.g. not via a shared database) |
58 | | -- Components should handle the entirety of their bounded context, for example persistence, logic and presentation (though obviously not all components will involve all of these layers) |
59 | | -- This pattern applies to existing services as well as greenfield development projects — please see Martin Fowler's [StranglerFigApplication blog](https://martinfowler.com/bliki/StranglerFigApplication.html) |
| 169 | +## Practical guidance |
60 | 170 |
|
61 | | -TO DO: reference to the NHS UI toolkit (for presentation fragments) |
| 171 | +- Split systems by bounded context, not by technical layers |
| 172 | +- Avoid shared databases between components |
| 173 | +- Ensure components interact only via public APIs (external to the internal service API) |
| 174 | +- Design services so a single team can change and deploy them independently |
| 175 | +- Align repositories, pipelines, and runtime ownership with team boundaries |
| 176 | +- Apply this pattern to existing systems as well as greenfield work, techniques like the [Strangler Fig](https://martinfowler.com/bliki/StranglerFigApplication.html) pattern can help evolve legacy systems safely |
62 | 177 |
|
63 | 178 | ## Examples |
64 | 179 |
|
65 | | -TO DO |
| 180 | +We have examples of live systems where this pattern has been applied in practice and continues to evolve. If you would like to explore concrete examples, including what worked well, what was learned, and where trade-offs were made, please get in touch. These systems can be discussed collaboratively and used as shared learning opportunities to help teams apply the pattern effectively in their own contexts. |
0 commit comments