Index

Decompose by Change, Not by Layer

A cancellation rule changes: refuse cancellation after warehouse handoff.

In a conventional layered application, the work touches a controller, an application service, a domain policy, a repository, a migration, and tests. If the organization mirrors those directories, one business decision becomes a project for API, backend, and database teams.

The next change adds a covering index for order lookup. It touches only the persistence layer and changes no business decision.

The folders are accurate. They are also answering the wrong question.

A useful decomposition starts with the behavior and owner which must move together when a business rule changes. Technical layers still matter inside that boundary. They are poor evidence for where independent ownership, release, or failure should begin.

Follow the reason for change through the stack

James Lewis and Martin Fowler’s microservices article contrasts technical-layer teams with services organized around business capabilities. It also names independent deployment as a defining property and notes the cost of remote calls and moving responsibility across service boundaries.

That combination matters. “Business capability” is not a nicer label for the same controller-service-repository split. It puts the broad stack needed for one business outcome under one boundary:

Ordering
├── HTTP input
├── cancellation application flow
├── cancellation policy
├── order persistence
└── order-facing tests and operations

The cancellation change can now remain inside Ordering. The index change also remains there, but it does not create a new business boundary merely because its mechanism is SQL.

AWS’s business-capability guidance defines a capability by what the business does to generate value and treats cross-functional ownership as an advantage. It also names the cost: finding capabilities requires actual business and subject-matter knowledge. A directory tree cannot supply that knowledge on its own.

A change ledger is better than architectural memory

People remember the awkward releases which support their preferred redesign. They forget the routine changes which stayed cohesive. A decomposition review needs a less selective record.

The synthetic SVC-01 fixture uses 24 dated changes across Ordering, Pricing, Fulfilment, and Payments. Each entry records the business reason, behavior before and after, components touched, decisions, data, transaction requirement, external effects, owner, release constraint, and provenance.

Eighteen changes form the discovery set. Six later changes are frozen as a holdout before candidate cuts are compared. The holdout does not predict a real future, but it prevents the analysis from redefining every boundary after seeing every example.

The ledger also includes evidence which makes the preferred cut look worse:

  • accepted cancellation releases a Fulfilment allocation;
  • accepted cancellation requests a Payments refund;
  • payment capture identity follows an accepted order;
  • delivery-discount ownership remains disputed between Pricing and Fulfilment; and
  • landed-cost presentation has no agreed owner.

It includes three technical controls too: an HTTP framework upgrade, one order index, and a mechanical formatter change. Counting those as domain co-change would turn tooling choices into service boundaries.

This is still synthetic evidence. It demonstrates the method and its failure checks. It says nothing about how Brian’s code or teams should be partitioned.

Four cuts face the same changes

The fixture compares technical layers, entity CRUD, business capabilities, and one undivided application. It does not calculate a quality score. For each change, it retains boundary crossings and hard transaction splits.

CandidateDiscovery changes crossingHoldout changes crossingAtomic splits
Technical layers16511
Entity CRUD821
Business capabilities510
Undivided application000

Technical layers perform badly for the expected reason: most business changes need input, policy, and persistence together. Eleven atomic capability changes would cross a layer boundary.

The entity cut looks more credible until promotion capacity changes. Quote calculation belongs to a Promotion-shaped component while persisted price rules belong to a Product-shaped component. The rule and usage reservation must commit atomically. One service per noun splits one Pricing invariant.

Business capabilities retain that invariant and explain the holdout changes better than either structural cut. They still have five discovery crossings and one holdout crossing. That is not a defect to erase. Cancellation really does coordinate with Fulfilment and Payments.

The undivided application crosses nothing because it contains everything. That makes it an essential control and an unhelpful automatic winner. Zero internal crossings do not establish clear decision ownership, independent testing, or a future release seam.

The result is a reasoning chain, not a leaderboard: capability boundaries preserve the observed atomic decisions, reduce incidental coordination, and continue to explain the holdout. The control stays cheaper to deploy.

Crossing a boundary can be correct

A good boundary does not make the business stop collaborating. It makes the reason for collaboration visible.

The cancellation flow crosses Ordering and Fulfilment because one context decides that cancellation is accepted and the other owns allocation release. Those decisions should not become one model merely to remove an arrow.

The fixture classifies coupling instead:

  • semantic coupling says one decision needs another context’s meaning;
  • transactional coupling says state must commit atomically;
  • temporal coupling says progress depends on timing or order;
  • data coupling exposes reads or writes across ownership;
  • release coupling requires contract coordination; and
  • operational coupling assigns failure diagnosis and repair.

Those categories lead to different responses. An atomic invariant argues for keeping behavior and data together. A temporal workflow may need a durable fact and retry policy. Semantic disagreement may need translation. A shared repair path needs an operator owner even when delivery is asynchronous.

AWS’s decomposition overview lists capability, subdomain, transaction, and service-per-team patterns separately. That is useful because no single heuristic carries every constraint. Co-change suggests cohesion; transactions and data can veto the suggested cut.

One cross-capability change does not prove that every boundary is wrong. It does prove that a tidy box diagram is incomplete.

Ownership cannot be mined from authorship

Git can show who edited a file. It cannot show who has authority to change a business rule, accept its operational consequences, or refuse a requirement.

The synthetic ledger therefore records decision and data owners explicitly. Ordering, Pricing, Fulfilment, and Payments have named ownership scopes. Landed cost and delivery discounts remain unresolved because the fixture has conflicting claims. Assigning them to the team with the most commits would replace a governance question with arithmetic.

Data ownership is equally strict. A component cannot write another capability’s state and still claim an independent lifecycle without an explicit boundary decision. Microsoft’s data-sovereignty guidance connects autonomous lifecycle and independent deployment to ownership of domain data and logic. It also allows one bounded context to map to one or several services. Semantic ownership is not a command to create one process per context.

When ownership is missing, the decomposition result should say so. “Backend” is not a business owner unless its authority over the named decision is real and explicit.

A module boundary is not yet a service

The evidence selects four capability boundaries for cohesion. The current deployment remains one application.

Ordering, Pricing, and Fulfilment stay internal modules because their synthetic records do not establish independent release and failure lifecycles. Fulfilment has a distinct scaling pressure and on-call owner, but that is not enough to justify a remote contract by itself.

Payments becomes a service candidate, not an approved service. It has explicit semantic and data ownership, independent release evidence, failure containment, and an on-call owner. The fixture still records one missing decision: compare the distributed transaction, latency, and failure costs with the modular form. That comparison belongs to the next article.

This distinction prevents a common substitution:

cohesive business boundary ≠ independently deployable service

Use a module when language and ownership are clearer than operational independence. Extract when independent release, scaling, compliance, or failure containment pays for network calls, versioned contracts, deployment machinery, observability, and repair.

The choice reverses when later changes repeatedly cross the proposed boundary, data cannot be owned honestly, the same people coordinate every release, or a new operational constraint makes independence valuable.

The fixture refuses eighteen shortcuts

The companion synthetic reproduction retains 22 passing tests, 24 changes, four candidate cuts, all six coupling types, four readiness records, four primary-source responses, and a score-free comparison.

Eighteen isolated counterfactuals try to select directories, technical layers, or one service per entity; remove a business reason or contradictory change; count formatting as domain evidence; tune the cut after holdout; infer owners from authorship or majority; split an atomic invariant; hide foreign data ownership; claim failure independence; erase all boundaries because one change crosses them; approve a service from crossing count alone; omit on-call ownership; present target ownership as current; orphan coupling evidence; or replace constraints with a weighted score. All eighteen fail.

The clean export reproduces from commit 82bc790fd35c42bddf59bd790d869f9c986ce82c. That proves the calculations over the declared synthetic ledger. It does not prove that its capability names, owners, or service candidate fit a real organization.

Before drawing the next service, take one accepted business change and follow it through policy, data, effects, and ownership. If those pieces cannot move together inside the proposed boundary, the service name is premature. If they can, keep the boundary modular until independent operation earns the network.