Two teams build the same thing — software that sells a ticket to a seat at an event — and finish with architectures that share almost nothing. One is a single program talking to one database. The other is a multi-region platform of subsystems trading events across a bus. Neither team made a mistake. The methodology that produced both is the same methodology, run against different answers to the same short list of questions. This module is that list, and what to do with the answers.
What this module does
The spiral climbed four altitudes and, at every one, surfaced architecture decisions it would not make. Per-use-case latency targets. Which recovery class a workflow reaches for. Whether subsystems keep separate stores. How they coordinate across a boundary. What the deployment surface looks like at a thousand venues. Each pass named these honestly and deferred them, because each altitude could only show where a decision arises and what it answers to. There is no altitude left above the system to defer to. The deferred decisions have piled into an inventory, and the inventory is this module’s agenda.
This is the point where the methodology stops surfacing decisions and starts making them. It is the one module that grows rather than shrinks. The spiral tapered as it climbed because each pass reused the vocabulary and added only its small delta, the telescope demonstrating itself in the book’s own shape. Architecture Synthesis is not a delta. It pays every deferred debt at once, so it swells back out. The short final spiral pass was correct precisely because the work it deferred lands here.
One distinction governs everything below. The spiral organized the work by altitude — use case, workflow, subsystem, system. This module organizes it by phase. Phase 4 elicits what the system must satisfy. Phase 5 selects the architecture that satisfies it. Phase 6 picks the concrete technology. The phases are not altitudes wearing new names; a single use case has all six phases, and a thousand-venue platform has the same six. The module owns Phase 4 and Phase 5, draws the line where Phase 6 begins, and presents the framework for changing an architecture over time that the Brownfield chapter then applies to an inherited system.
One note before the work starts. Since this book’s second edition, the module’s method has grown into a book of its own — Architecture Synthesis: The Next Correct Step — which carries what this preview only names: the full ledger of axis values and costs, the derivation procedure with its conflict rule and halts, the verification arithmetic, and four blind derivations graded in the open. The two share one question sheet. For architecture work, start there; this module remains the bridge from the spiral to that book, and nothing it teaches is contradicted on the way.
The deferred-decision inventory
Before the framework, the checklist. Every promise the spiral made to resolve something “in Architecture Synthesis” is collected here, so none is quietly dropped. Most map to a section below; reading the list is the fastest way to see the module’s shape.
- Recovery-class selection — all four judgment considerations weighed, mixed strategies per use case, workflow, subsystem, and boundary. (Recovery-class selection.)
- Phase-4 elicitation — the full question set; per-use-case, per-workflow, and per-subsystem targets as architecture inputs, a subsystem’s target being the derived envelope of its workflows’. (Phase 4.)
- Persistence configuration — per-subsystem stores versus a shared store with logical separation, and the topology underneath. (Phase-5 vector, persistence axis.)
- Composition substrate — direct, event-based, streaming, or mixed; how units compose at any altitude, from the steps inside a use case to the wire between subsystems that carries typed, versioned facts. (Phase-5 vector, substrate axis.)
- Deployment topology — one deployable, many, a unified runtime, functions. (Phase-5 vector, deployment axis.)
- Cross-data-class consistency — what each data class requires, strong within a subsystem and eventual across, and the substrate that delivers it. (Phase-5 vector, state and substrate axes.)
- System resource provisioning and technical cross-cutting — observability, tracing, idempotency, supplied uniformly. (Phase-5/6 boundary.)
- Aspect-wrapping convention — which technical Aspect wraps which, and in what order. (A cross-cutting note, below.)
- When-to-decompose judgment — the triggers that justify splitting a unit out. (A decomposition-granularity heuristic, below.)
The last two never had an obvious home in the spiral. They get one here.
Phase 4: eliciting what the system must satisfy
Phase 4 asks what the system must be true of, before any architecture is on the table. The discipline that mattered through the whole book matters again: name the questions, keep the set small, and resist the urge to invent a special question per project. The minimal set is universal across enterprise backend work. The answer varies wildly between a single venue and a global platform; the question does not.
There are nine questions, in four categories: service-level objectives (SLOs), constraints, operational targets, and substrate-shaping forces. The set is shared with this book’s architecture companion (Architecture Synthesis), where each question earned its seat under an audit; the changelog carries the transition from this module’s earlier eleven.
| # | Question | Category |
|---|---|---|
| 1 | Time budget — latency per use case, usually P95/P99; completion windows for batch | SLO |
| 2 | Failure budget — availability per use-case criticality | SLO |
| 3 | Loss budget — durability, the recovery-point objective, retention | SLO |
| 4 | Consistency contract, per data class (strict / read-your-writes / bounded-staleness / eventual) | SLO |
| 5 | Load — magnitude at steady state and peak, shape per path (read-heavy, write-heavy, event-heavy, mixed), concentration, window | Substrate-shaping |
| 6 | External constraints — compliance regimes, residency, mandates that strike values outright | Constraint |
| 7 | Release structure — deploy cadence divergence and deploy safety | Operational target |
| 8 | Cost and capacity envelope — a ceiling or a per-operation budget, and who operates | Operational target |
| 9 | Multi-X — multi-country, -currency, -tenant, -region | Substrate-shaping |
These are the system’s qualities, and they are not one kind of thing. The four categories above drive different decisions, so a single label flattens exactly the distinctions Phase 5 turns on. Substrate-shaping in particular earns its own category rather than hiding among constraints: multi-X partitions the data, and load’s shape decides what the substrate has to be, before any single target is read.
The answers attach at four scopes, and the scope is part of the answer. The time and failure budgets attach per use case: buying a ticket and quoting a price have different targets. The consistency contract and the loss budget attach per data class: a booking and a price quote tolerate very different staleness. Load attaches per path, because one system is routinely read-heavy at one tier and write-heavy at another. External constraints, release structure, the cost envelope, and multi-X attach per domain, describing the whole product rather than one operation. A subsystem’s target is rarely stated directly; it is the derived envelope of its workflows’ targets, the tightest each axis demands.
Phase 4 does not own the trigger. What makes a use case run is Phase-1 territory, settled when the use case was identified. When Phase 4 surfaces a use case nobody had written down, the response is targeted insertion — fold that one use case back into Phase 1 and re-derive only it. Not a full re-derivation. The minimal set is a floor, not a ceiling; contextual questions ride along when a domain demands them — recovery-time objectives, an explicitly contracted external dependency, a backwards-compatibility window, seasonal load, the shape of audits and disputes. They are illustrations, not a longer mandatory list.
Phase 5: the architecture as a vector
Architecture is not picked from a catalog of named styles. It is a vector across orthogonal axes, each axis a decision the Phase-4 answers press on. Six axes describe the space the methodology commits to.
| Axis | Values |
|---|---|
| Deployment topology | single deployable / multiple deployables / unified runtime / serverless / hybrid |
| Composition substrate | direct / event-based / streaming / mixed |
| Read/write model | unified / separated (read model split from write model) |
| State storage | current-state / event-sourced / hybrid |
| Persistence configuration | single shared / distributed shared / sharded shared / per-component / polyglot / hybrid |
| Recovery class | BER / FER / design-out / mixed-by-component |
The recovery-class values are the recovery triple Foundations introduced, carried here so the walkthrough can use them: BER (backward error recovery, compensating by an inverse), FER (forward error recovery, continuing with degraded state), and design-out (reshaping the domain so nothing needs recovering). The recovery section below revisits how to choose among them.
A few decisions are already settled in how the catalog reads. A layered, ports-and-adapters arrangement is not on the list, because it is a composition style — how a unit is built inside — not a topology of the whole. The unified runtime is on the list as a first-class topology: a single runtime that hosts every component as a slice, makes the wire between them transport-transparent, and supplies resources uniformly, so the same composition deploys as one process or many without touching a line of business code. Aether is the reference instance of the class the book has in mind, and the slice is its deployable unit. Modulith versus microservices is not a seventh axis; both are points on the deployment axis, one deployable against many, with the same internal subdomain split.
The stakes on that axis are operational. The number of deployables is the dominant operating cost a topology carries — on-call rotations, infrastructure, the debugging that crosses process boundaries — so keeping one-versus-many deferrable means not paying for distribution until the business actually forces it. Where a stack offers no unified runtime, that deferral is gone: the team commits at design time, to a modulith whose later split is a rewrite of the wire, or to services that carry the coordination cost across cores that would rather be one. The methodology names the topology either way; whether the choice stays deferrable is the stack’s to answer, not the method’s.
The event-based substrate keeps the same choice deferred, by its own route. Because handlers couple to events rather than to each other, they bundle freely: one deployable, several, or a single feature-flagged artifact running only the handlers a deployment needs. One-versus-many stays a packaging decision, made and remade without touching a handler.
What Phase 5 is for is worth stating plainly, because it answers the oldest objection to a methodology this deterministic. Phase 5 is where the system’s qualities — latency, scale, availability, durability — shape the substrate. Process simplicity is not one of the qualities traded here. It was settled at Phase 1, when the process was decomposed into named steps and typed outcomes, and nothing in Phase 5 spends it. The performance question and the simplicity question live in different phases and compose; they do not trade against each other. Where a tight loop genuinely needs raw speed, it isolates inside a Leaf and the use-case body above it is unchanged. Hard real-time, where the deadline is the correctness criterion, sits outside the methodology’s bounded scope, and the book says so rather than pretending otherwise.
The axes are not fully independent. Some combinations are coherent and some defeat themselves, and the methodology surfaces the incoherent ones as contrast rather than as a chapter of prohibitions. Many deployables wired together with direct calls everywhere is a distributed monolith: it pays the latency and operational tax of distribution and keeps none of the autonomy. Serverless functions carrying heavy in-memory state is a mismatch of the topology to the workload. Event-based composition laced with direct inter-service calls at every hop defeats the decoupling the events were chosen for. Give each handler its own store and its own deployable and you pay, in eventual consistency and a partitioned store, for an independence free bundling already provided: the cost is real, the benefit is not new. These are not rules to memorize; they are what the walkthrough will run into and name as it goes.
A convergence worth noting. Derive the deployment and substrate options from the methodology, as above, and you arrive at a small set: one cohesive program, a split by level of abstraction, a split by subdomain, a one-directional flow of processing stages, many identical instances. The literature on architectural structure arrived at the same set independently and named them — Monolith, Layers, Services, Pipeline, Shards — with coordination layers added on top when instances or services must be coordinated. Denys Poltorak’s Architectural Metapatterns (CC BY 4.0) catalogs these and argues their completeness on geometric grounds.
The recognition is corroboration, not foundation. It reaches only the first two axes; read/write model, state storage, persistence, and recovery sit outside that structural taxonomy, and the unified runtime has no counterpart in it at all. The partial overlap is the honest result, and the divergences are where the methodology is doing its own work.
Selecting the vector: thought process, not flowchart
How a team gets from nine answers to six axis values is the heart of the method, and it is deliberately not a procedure that hands back an answer. A decision tree per axis was considered and rejected: it reads like a recipe and it misses the interactions between axes, which is where the real judgment lives. A compatibility matrix of every axis against every other was considered and rejected too: it explodes combinatorially, it goes brittle the moment an axis gains a value, and its false precision invites the reader to trust a lookup where they should be thinking.
What replaces them is a small set of considerations per axis, each anchored in a Phase-4 input, phrased as consider this as well rather than pick from this menu, demonstrated by working real profiles end to end. The stance, stated once: show the thought process, give the reader a tool for building their own thought process, and let them make their own decision. The walkthrough below is where the considerations are shown in motion. Only afterward are they named and collected, because a heuristic read before it has been seen at work is a rule, and rules are what this method is trying not to ship.
Three profiles, one domain
The same domain, event ticketing, three times, at three scales. Not three domains chosen to suit three scales — that would smuggle in the assumption that a domain belongs to a scale, which is exactly the assumption the methodology rejects. A single venue can run the small profile and a global entertainment company can run the large one, and the domain is identical underneath. What differs is the Phase-4 answers, and the point of the walkthrough is to watch different answers drive different vectors, with no step that says “this is the architecture for ticketing.”
Profile one: the independent venue
One venue runs its own box office. A single team owns the software, the deployment is one region, the scale is modest — bookings in the tens per second on a busy night, availability checks a little higher. Phase 4 reads quietly. Buying a ticket should settle in around two seconds at P95; checking a seat’s availability should answer below a second. Availability of the service, 99.5% — a few hours a year of downtime is survivable for one venue. Booking data is strictly consistent with a recovery-point objective of zero, because losing a confirmed sale is unacceptable; availability and price-quote reads tolerate eventual consistency. One country, one currency, no multi-X. Daily deploys. A tight cost ceiling, because one venue has no budget to waste. Load is read-moderate, with no continuous event stream to speak of.
Read those answers slowly, one axis at a time, in the order the Phase-4 inputs press on it. A single team with a tight complexity budget and one region settles three axes almost at once. The team has neither the headcount nor the operational appetite for many deployables, so deployment topology is single deployable; once that is settled, the substrate axis follows, because event-based composition earns its cost across deployment boundaries and there are none to cross, leaving direct composition as the cheaper and simpler answer; and a single team with modest scale and one region has no reason to split persistence, so single shared store falls out for free. The next two axes are decided by what the load is not. There is no separate scaling pressure on reads, so the read/write model stays unified — separating it would be machinery the load never asks for, and the team would be paying for projection sync against a problem it does not have. There is no audit-or-replay requirement either, which keeps the storage axis at current-state, with no event log carried for its own sake. The last axis is decided by what the domain forces. Booking moves money and must undo a half-finished purchase cleanly, so the recovery class is BER: reserve the seat, authorize the payment, confirm; if a later step fails, release and void.
The vector: single deployable / direct / unified / current-state / single shared store / BER. The instructive part is what the team did not do. The temptation, in any age that has read about large platforms, is to jump to many services backed by an event store because that is the architecture in the article. The Phase-4 answers do not justify the cost. Copying a big platform’s vector into a small platform’s Phase-4 answers is how the small platform drowns, and the methodology’s defense against the copy is the discipline of reading the answers first.
Profile two: the regional platform
Now several venues, several teams, a region rather than a city, more than one currency. Phase 4 tightens in specific places and stays loose in others. The customer-facing availability check must answer below 500 milliseconds at P95, because shoppers feel it; buying still settles around two seconds; the feed of sales events can be ingested asynchronously. Booking stays strictly consistent with a zero recovery-point objective; price quotes are allowed bounded staleness; a customer’s own seat-availability view wants read-your-writes, so they never see a seat they just released as still held. Availability of the service rises to 99.9%. Deploys are weekly, blue-green. Multi-currency and multi-country are now real. The load is read-heavy and event-heavy. Card payments bring a compliance regime with them.
The answers no longer point one way on every axis, and this is the first real lesson of the walkthrough: a hybrid vector is not a failure to decide, it is what a real system of this size looks like. Several axes resolve under genuine tension this time, not freely as in the small profile, and naming the tension is part of the work.
Topology is the first tense one. Three or four teams gaining independence push toward many deployables, because team independence is a real force and the methodology takes it seriously. Deployment complexity still being bounded pushes the other way; the org chart is not yet so large that paying for many pipelines, many on-call rotations, and many cross-deployment debugging trips is justified. The resolution is a modulith: one deployable, split internally along subdomain boundaries so the teams own modules without yet paying for independent deployment. Modules become services later, when independence of release actually demands independence of deployment, and not before; the methodology resists pre-committing to the latter for the sake of the former. The substrate axis tenses next. Direct calls inside a module are cheap and consistent and serve the strict-consistency parts of the system well; the things that tolerate propagation lag — a price change being noticed by the search index, a sales-feed update arriving at the dashboard — are exactly the things event-based composition handles without paying the latency tax of a direct call. So the substrate goes mixed: direct within a module, event-based across. A team that picked one substrate uniformly here would be fighting at least one of these forces, and the cost would show up either as latency where it does not belong or as coupling where the modules want to evolve apart.
Storage is where the most common trap of this scale lives, and the walkthrough has to name it to teach. The PCI compliance regime and the dispute-reconstruction pressure together suggest an audit requirement, and the temptation is to reach for event-sourced storage because event sourcing is what one reads about when one reads about audit. The Phase-4 answer is narrower than the temptation. The system needs an audit log, not a full replay of every state transition; the read model is still fundamentally the current state of bookings and prices. So storage stays current-state, with an audit log kept alongside as data — written by the same use cases, queried separately for the audit and dispute paths. This is not event sourcing in disguise, and the discipline of keeping it from drifting into one is the lesson: an audit log answers a regulatory question; event sourcing answers a replay-and-derive-projections question, and the two are not the same shape of requirement. Read/write stays unified for related reasons. Reads are heavy but their shape is the same as the writes; a separate projection model would buy nothing the current state plus read replicas does not already give. Persistence goes to a single shared store with read replicas, the replicas carrying the read-heavy availability traffic without the operational complexity of per-component stores at a stage when no team yet owns one cleanly. Recovery splits cleanly by what each part is. Booking, with its money and its sequenced steps, stays BER. Availability and reservation, where the question is whether a counter or a hold is correct under concurrent traffic, becomes design-out: an idempotent reservation cannot double-book on retry, and an eventually-consistent counter converges without compensating anything. Design-out is the cheapest recovery class here because its cost is paid once, in the shape of the domain, rather than over and over in a compensation path. That is not the same as free: the hold that removes the booking race adds hold expiry to manage, and the idempotent counter adds a convergence window to bound.
The vector: modulith / mixed substrate / unified / current-state with audit log / single shared with replicas / BER and design-out. What the team avoided this time was splitting into independently deployed services because the org chart had grown. Team independence is a reason to draw module boundaries; it is not yet a reason to draw deployment boundaries. The modules become services later, when independence of release actually demands independence of deployment, and at no earlier point. The other trap avoided was the slide from “we need an audit log” into “we should event-source the whole store” — the audit-versus-replay confusion the storage paragraph already named.
Profile three: the enterprise multi-tenant platform
The platform the spiral ended on: a thousand venues, many tenants, multiple regions, contractual targets the business is held to. This is where the running example has lived since the last pass, three subsystems already in view — booking, pricing, and event management — exchanging typed, versioned facts. Phase 4 is now demanding and uneven across the surface. The price-quote and availability read paths carry a contractual P99 below 200 milliseconds. On-sale moments are violent: the instant a sought-after show opens, attempts arrive in the hundreds of thousands per minute against a handful of seats. Price-change history must be auditable and replayable, for regulators and for disputes. Booking is strictly consistent with a zero recovery-point objective; availability reads carry bounded staleness with an explicit lag bound the contract names. Service availability on the customer-facing read path is 99.99%. Regions are multiple, with data-sovereignty rules. Compliance spans payment-card and consumer-protection regimes. Deploys are weekly with canaries. The cost ceiling bites at scale. Load is read-heavy and event-heavy and write-heavy at once, the write spikes concentrated in the on-sale bursts.
No single vector satisfies all of that, and the methodology does not pretend one does. It produces a vector per subsystem, which is the telescope showing up one last time: different altitudes, here different subsystems, can carry different vectors coherently. The subsystem boundaries the spiral surfaced are the boundaries along which the axis answers genuinely differ, so giving each subsystem its own vector is not a hedge, it is the correct shape.
Start with the topology, because the topology decision sets the substrate the rest will use. The booking core is strongly coupled internally (reserve, authorize, confirm) and strictly consistent within the saga; it deploys and scales as one unit. Pricing’s core (price-history writes, schedule changes, the projection-update path) is similarly internally coherent. Event management is mixed but coherent. These three cores want to talk to each other transport-transparently and want the option to deploy as one process or many without rewriting business code, which is exactly what a unified runtime is for; the cores become Aether slices on the runtime. The read path is something else entirely. Quote and availability under an on-sale burst has a scaling shape unlike booking’s by orders of magnitude, and it has its own latency SLO the contract names; leaving it inside the unified core means scaling that core for the read profile, which is fighting the shape of the load. So the read path splits out as services in front of the unified core, scaled on its own profile, deployed on its own cadence. Splitting it out also contains blast radius: a thundering herd on the read path cannot starve the booking saga of resources it shares. The result is a hybrid topology by necessity, not by indecision; uniform here would mean either scaling the cores for the read burst or hosting everything in services and paying coordination cost on the strongly-coupled cores.
The write side has the opposite shape. A sought-after seat is a single contended resource, and the hold serializes access to it by design. That serialization is a throughput ceiling on the hot seat, but an inherent one: one seat has one winner, so the burst is absorbed by fast-failing the losing attempts with an immediate SeatUnavailable, not by parallelizing a write that cannot be parallelized.
Substrate falls out next. Across subsystems the wire is event-based carrying typed, versioned facts: pricing publishes “the schedule for this show changed,” event management publishes “the show went on sale,” booking subscribes and reads what it needs. Cross-subsystem coordination tolerates propagation lag at this scale, and a direct call across a subsystem boundary at this scale would be paying latency for autonomy that the methodology is trying to keep. Within a subsystem the substrate stays direct where strict consistency demands it (the booking saga’s steps must see one another’s effects); the substrate is mixed because the forces are mixed. The wire carrying facts rather than commands matters: subsystems publish what they know to be true, not requests for other subsystems to act, so the typed-versioned-fact contract is what each subsystem is responsible for, and what every other subsystem reads at its own pace. Cross-subsystem transactions never form, because there is nothing to coordinate transactionally; the substrate makes that shape impossible.
Storage and read/write model decide together, per subsystem, by the same audit-versus-replay distinction the modulith profile already had to make, only sharper. Pricing carries a regulatory replay and dispute-reconstruction obligation that is a genuine event-sourcing shape: prices change continuously, the history must be reconstructable, and the read views are projections derived from the event log. So pricing is event-sourced, with separated read models serving the quote storm from projections kept fresh by the same events. Booking is the contrast that makes the lesson visible. Booking’s audit obligation is the same kind the modulith carried — an audit log answers the question — and event-sourcing the booking core would import the costs the modulith profile already refused, against a question current-state plus audit-as-data answers. So booking stays current-state, with audit-as-data, with unified read and write. The slide from “we adopt a durable-workflow platform for booking” into “we have event sourcing now” is the same trap a level up, named here so the team can refuse it. Event management sits in between, current-state for its operational data and event-sourced for the schedule history that auditors ask about; storage goes hybrid at the platform level, because the data classes themselves are.
Persistence follows storage, with multi-region pressure as the new force. Booking’s strict consistency across regions wants a distributed shared store that gives multi-region transactional guarantees; this is the only place at this scale where that property is non-negotiable. Pricing owns its event log and its projections cleanly, so per-component stores fit, and they let pricing’s persistence tech be chosen for what it stores rather than for what booking stores. Event management’s mixed document and relational shapes argue for polyglot: a document store for show metadata where the schema is naturally nested and variable, a relational store for the venue-and-seat structure where the schema is rigid and the joins are real. Persistence at the platform level is hybrid by composition, not by hedge.
Recovery, last and load-bearing, mixes by subsystem because the domain shapes do. Booking is BER, the saga — money has defined inverses (void the authorization, release the hold) and the inverses stay in-domain. Pricing is design-out because its append-only log and its idempotent application make compensation unnecessary; replaying an event is harmless, and the design-out cost is paid once in the model rather than over and over in compensation paths. Holds on contended seats are FER, time-as-decay: a hold’s state moves through fresh → stale → expired, and the application reads the decay state and acts on it (a stale hold can be refreshed; an expired one is gone, no compensation needed). FER reads as the right answer here because forward progress with degraded state beats halting until consistent, and because the decay itself is what the domain models. Three recovery classes coexisting at the platform level is not a compromise; it is each subsystem recovering the way its own domain demands.
The vector, written out: services for the read path plus a unified runtime for the cores / event-based across, direct within / separated read model for pricing, unified for booking / event-sourced for price history, current-state for booking / distributed-shared plus per-component plus polyglot / BER and design-out and FER. Every “plus” is a place the team declined a tempting uniformity. It did not force a separated read model onto booking, where the write path dominates and a read projection earns nothing. It did not event-source the booking core, for the reason the modulith profile already established. It did not put everything in services, because the unified runtime hosts the strongly-coupled cores more cheaply than a mesh of services would. It did not put everything in the unified runtime either, because the read path’s scaling shape does not belong inside the cores. The hybrid is not a compromise the methodology was forced into. It is the accurate shape of a system whose parts have genuinely different Phase-4 answers.
What the three profiles together demonstrate
Walked in sequence, the three profiles say four things the methodology has been claiming all along, now visible at once. They demonstrate that Phase-4 inputs drive Phase-5 vectors: same domain, three sets of answers, three architectures that share almost nothing. They show that hybrid is normal at any real scale: only the small profile resolved uniformly on every axis, and the moment several teams, several regions, or several data shapes appear, the axes start pulling in different directions and the correct vector is the one that lets them. They show the telescope reaching architecture: the enterprise profile gave each subsystem its own vector, which is the same recognition the spiral has been making at every transition, applied one more time. And they show anti-patterns surfacing as contrast, not as a separate chapter: each profile named what it avoided and why, and the methodology never had to prohibit anything in advance because the walkthrough is what makes the incoherent vectors visible.
What the walkthrough did not do, and what the next section does, is name the considerations that were operating per axis. Reading them after the walkthrough rather than before is the methodology’s own discipline applied to itself: name comes third, after the situation and the derivation. A heuristic read before it has been seen at work reads as a rule, and rules are what the method is trying not to ship.
The heuristics, named
The three profiles were not consulting a lookup table. They were running the same short set of considerations on each axis, and letting the Phase-4 answers decide. Named after the fact, the considerations are these. Read them as also worth weighing, never as a flowchart that returns a value, and notice that each one points back at a numbered Phase-4 input.
Deployment topology — primary inputs: load (#5), multi-X (#9), release structure (#7), cost envelope (#8).
- Single team, bounded complexity budget, one region → a single deployable.
- Several teams gaining independence, deployment complexity still bounded → a modulith, split by subdomain inside one deployable.
- Strongly-coupled subsystems that scale together and want a transport-transparent wire → a unified runtime.
- One part with a scaling shape unlike the rest (a high-fanout read path) → split that part out and leave the coupled core whole. The result is a hybrid, and that is fine.
Composition substrate — primary inputs: time budget (#1), consistency contract (#4), load (#5).
- One deployable, latency budget allows, no cross-unit fan-out → direct composition.
- Cross-unit facts that tolerate propagation lag → event-based composition.
- A continuous, ordered, high-volume feed → streaming for that feed.
- Strict consistency inside a unit, lag tolerated across → mixed, and expect mixed at any real scale.
Read/write model — primary inputs: load (#5), time budget (#1), consistency contract (#4).
- Reads and writes at similar volume, no separate read SLO → unified.
- A read path with its own tight latency target and tolerable staleness → separate the read model, for that path only.
- Do not separate the model on a write-dominant strict path; the projection earns nothing and costs synchronization.
State storage — primary inputs: loss budget (#3), external constraints (#6), consistency contract (#4).
- No audit-or-replay requirement → current-state.
- Regulatory audit plus replay or dispute reconstruction → event-sourced, for that data class.
- Audit needed but not replay → current-state with an audit log kept as data; do not let it slide into full event-sourcing.
Persistence configuration — primary inputs: load (#5), multi-X (#9), external constraints (#6), loss budget (#3).
- Single team, single region, modest scale → a single shared store.
- Multi-region transactional guarantees → a distributed shared store.
- Per-team independence and heterogeneous data shapes → per-component or polyglot.
- Hot-path reads with acceptable eventual consistency → polyglot, with a store shaped to the read.
Two debts from the spiral attach here, because both govern how the vector is even shaped.
The first is the Aspect-wrapping convention. When several technical Aspects wrap one operation — measuring its duration, bounding its time, breaking its circuit, retrying it, limiting its rate — the order is a settled convention, not a per-use-case decision. Outermost sits the metric, so it measures everything inside, including the waiting; then the timeout; then the circuit breaker; then retry; then the rate limit; and innermost the business operation. The runtime supplies this wrapping uniformly, the business code never names any of it, and because the order is conventional rather than chosen each time, every operation on the platform is instrumented the same way. The decision is real but it is made once, at the platform level, and inherited everywhere.
The second is the when-to-decompose judgment — how to know a unit should be split out into its own workflow, subsystem, or deployable rather than left whole. Four triggers justify a split, and the absence of all four argues against one: a distinct trigger that fires the candidate independently; a distinct SLO that the candidate must meet on its own; non-trivial compensation that the candidate carries, so its failure handling is its own concern; and independent operation, a lifecycle that deploys, scales, or fails apart from its neighbors. A split justified by none of these is granularity for its own sake, and it imports coordination cost without buying anything back. The read path in profile three earned its split on a distinct SLO and a distinct scaling shape; the modules in profile two did not, yet, earn deployment splits because none operated independently.
Step composition: one use case, two substrates
The composition-substrate axis is the one most often mistaken for a change of altitude, so it is worth walking on a use case the book already has. Buy ticket in Pass 1 composed its steps directly: hold the seat, authorize the payment, confirm the reservation, issue the ticket, each step’s typed result feeding the next inside one use-case body. That is direct step composition, the book’s default because it is the simplest thing that works under a single-deployable vector.
The same use case can compose those steps event-based. Hold seat publishes a SeatHeld fact; authorize payment is triggered by it rather than called; authorize payment publishes PaymentAuthorized, and so on down the chain. The steps are unchanged; what changed is that each is now reached through a published fact instead of a return value.
Under both substrates the same thing travels between the steps: each step’s result is the next step’s input. Direct composition holds that result implicitly and briefly, on the call’s own stack, gone once consumed; event-based composition externalizes the identical result into a published fact that outlives the step producing it. The axis is not two kinds of computation but one in-flight state held at two durabilities — which is why the event-based substrate’s cost is precisely the cost of making between-steps state durable, named, and independently consumable.
What does not change is the altitude. Buy ticket is one customer action, and it stays one use case under either substrate.
What event-based composition does surface is operational. Each step now carries a real external trigger, the fact the previous step published, so it can be deployed, scaled, and operated on its own, and the compensation chain becomes a registered Aspect rather than inline handling. Those properties are worth their cost exactly when the four when-to-decompose triggers above hold: a distinct trigger, a distinct SLO, non-trivial compensation, independent operation. When they hold, the event-based substrate earns itself; when they do not, direct composition is the cheaper answer and the use case stays a single Sequencer. The substrate is the choice; the use case is the same either way.
Recovery-class selection
Recovery is the sixth axis, and the one axis whose selection procedure was already built rather than authored here, so the walkthrough could lean on it freely. There are three responses to an operation being invalidated after earlier work has already changed state, and most discourse names only the first.
- BER, backward error recovery: compensate by an inverse. Release the held seat, void the authorization, post a reversing entry. The classic rollback and saga shape.
- FER, forward error recovery: continue with degraded state rather than undoing. Serve a default, queue a notification for later, let a value decay through fresh → stale → expired and act on its decay state.
- Design-out: change the model so the invalidation cannot arise. An append-only log corrected by appending, a reservation model where two bookings of one seat are structurally impossible, an idempotent operation safe to repeat.
Which one fits is a judgment across four considerations, weighed, not fed into a tree: whether the effect has a defined inverse; whether staying running degraded beats halting until consistent; whether the operation can be made commutative, idempotent, or immutable by reshaping its domain; and the cost of coordinating compensation across autonomous parts. Mixed is the normal answer, and the platform showed it — BER for money, design-out for the pricing log, FER for decaying holds, composed without friction because each subsystem recovers the way its own domain demands.
Compensation itself comes in two grades. When the inverse stays inside the step’s own domain (insert and delete, reserve and release, post and reverse) it is derivable, near-mechanical. When the effect escapes the domain, as a confirmation email whose inverse is a retraction email, the compensation is designed per case, and the inverse is itself a use case with its own steps and failures. Some effects leave a residual that no inverse fully clears: money can be refunded, the hour the customer spent cannot. The methodology surfaces the residual during decomposition rather than hiding it behind a label like “partial inverse.”
The saga is not a primitive. It is BER applied across autonomous steps that share no transactional substrate, where each forward step’s inverse is itself a use case. It is a compensation shape, recognized by its structure rather than pinned to an altitude — most elaborate at workflow altitude, across subsystems, but the same shape wherever committed steps must unwind. What looks like a higher saga is a composition of sagas. And time deserves its own note, because it plays three different roles that are easy to blur. Time as a trigger fires a compensating use case at a moment — a lease expires, a hold is released — which is BER. Time as a condition is a bound checked at read time, a boundary test, not a compensation. Time as decay is state degrading continuously through named stages, which is FER, and naming continuous time-driven decay as a first-class recovery shape is one of the methodology’s own contributions rather than something borrowed. Across a subsystem boundary, prefer a read to a write; where a cross-boundary write is unavoidable, prefer design-out over a compensation that has to reach across autonomy to undo itself.
Phase 5 to Phase 6: where technology enters
Phase 5 picks the axis vector. Phase 6 picks the concrete technology that realizes it. The line between them is the line between selecting the restaurant and selecting a meal from its menu. “Event-based composition” is a Phase-5 value; which broker carries the events is Phase 6. “Distributed shared store” is Phase 5; which product provides it is Phase 6. The methodology lives upstream of the substrate choice and stays there.
The line has an honest wrinkle, and pretending it is clean would be worse than admitting it. Some axis values name properties that arrive bundled in a recognized product class — a unified runtime, a distributed shared store — so choosing the property and choosing a product feel close together. The boundary still holds: Phase 5 commits to the property because the Phase-4 answers demand it, and Phase 6 then picks among the products that supply it. Acknowledging the overlap reads more honestly than drawing a fictional clean line.
A durable-workflow platform is the sharpest case of a Phase-6 choice that bundles a Phase-5 vector. Adopt one and you have implicitly chosen event-sourced workflow execution, managed retries, built-in compensation, and a distributed-shared persistence underneath. That is a coherent vector, and the methodology adds no new primitive to describe it: a durable workflow is a state-machine Condition driving a Sequencer, with state persistence supplied as an Aspect; its activities are Leaves; its timers are a scheduler resource firing a trigger. The saga reduction holds inside it unchanged. Three traps come with the choice, and naming them is the methodology’s contribution to using it well. Treating everything as a workflow, when most operations are a plain Sequencer that needs none of the machinery. Reaching for event-sourcing by the back door, adopting the platform for durability and inheriting an event store the domain never asked for. And letting workflow versioning become a load-bearing operational concern that nobody planned for — a long-running workflow outlives the code that started it, and migrating in-flight instances across versions is a real Phase-5 cost that deserves a mitigation plan, not a discovery in production.
Architecture as a derivative
The last piece of the framework reframes everything before it. An architecture is not a state a system is in. It is a derivative — the next correct step from where the system stands, given the forces on it now. The methodology’s output for a living system is a next-correct-step recommendation, not a target-state migration plan, because for any system that keeps living the transformations never stop. This module presents that framework; the Brownfield chapter applies it to a system inherited mid-flight.
Every transformation is driven by a change in the Phase-4 answers. The drivers come in three shapes, and they are entry paths into the same procedure, not three different procedures. An external change arrives — a new region, a new regulation, a tenfold growth in load — and the affected Phase-4 questions are re-elicited. An always-was-wrong discovery surfaces internally, and the response is a bounded audit: re-ask the minimal set against the current architecture, scope the audit to the shape of what was discovered, and resist the reflex to rewrite everything. A new altitude telescopes open as the system grows, deriving its own Phase-4 surface where the pressure lands. All three end in the same place: the next correct step on the highest-impact gap.
Each candidate step is weighed against six cost-and-risk indicators, qualitative, never scored into a false number: the reversibility of the step, the feasibility of the intermediate state while the system runs through the change, the coupling cost of coordinated changes elsewhere, the duration the system spends in dual state, the dependency on prior transformations, and the failure-mode amplification an irreversible step carries, which is what earns a step its mitigation plan. The literature on architectural evolution maps the same terrain from the other side: catalogs of transitions, each with its prerequisites and its costs, where a transition’s prerequisites read as the methodology’s intermediate-state feasibility and dependency cost, and its listed downsides as failure-mode amplification. Those maps are a useful source of transition shapes and a sanity check on the indicators, revoiced into the methodology’s terms. The bulk of that work belongs to Brownfield, where an inherited system actually needs them.
Sometimes the honest output is that there is no good next step, and saying so is itself useful. Five failure modes account for it. The Phase-4 inputs may contradict — require a thing and its negation at once. No vector may be feasible against all current inputs. The system may be in a trapped state, where cyclic dependencies admit no coherent next step, and the resolution is to invest in scaffolding first: test substrate, observability, deployment automation, the things that make a next step possible. The inputs may have a knowledge gap the team must close by elicitation. Or the situation may be unexplored territory the methodology was never built for — it is bounded to enterprise backend, and outside that bound it hands the reader back their own judgment rather than pretending to cover them. Surfacing the obstruction is the useful output; a methodology that always returns a confident answer is hiding the cases where it has none.
Closing: the first derivative
The spiral surfaced the decisions; this module made them. The nine questions of Phase 4, the six axes of Phase 5, the line where Phase 6 begins, the three recovery classes and the judgment that picks among them, and the framework that treats every architecture as a step rather than a destination. The three-profile walkthrough showed the whole of it in motion: one domain, three sets of answers, three architectures that share a method and almost nothing else, and not one moment where the architecture was chosen rather than derived.
The reframe at the end points the way forward. If even a greenfield architecture is only the first derivative — the next correct step from a blank page, given the answers in hand — then the system that arrives already built, already running, already carrying decisions nobody alive remembers making, is the same problem from a harder starting point. The Brownfield chapter takes it up: the reader who inherits the system rather than designs it, and who needs the next correct step most of all. It is the harder version of the problem, and by far the more common one, because most working systems are inherited rather than begun.