Cloud Native and Service Boundaries
In this fictional scenario with illustrative counts and timings, a team splits a monolith into eleven services over eighteen months. Deployments are still coordinated because six services read the same database tables. A breaking schema change requires four teams in a room. The test suite that used to run in nine minutes now needs eleven services running together, and an outage takes longer to diagnose because the request path crosses seven processes.
The team separated processes while leaving important dependencies intact. Understanding those dependencies explains both where service boundaries help and what operating them costs. The same questions apply before the split, when boundaries are still easier to move inside a codebase.
The definition is about coupling, not containers
Cloud native has a broader meaning than running on Kubernetes. CNCFโs definition v1.1 describes programmatic, repeatable operation at scale and characterizes the resulting systems as loosely coupled, secure, resilient, manageable, sustainable, and observable.
Its technology examples include containers, service meshes, multi-tenancy, microservices, immutable infrastructure, serverless, and declarative APIs. The list is illustrative. Adding those technologies does not establish the operating qualities or remove the dependencies in the opening example.
CNCF associates these practices, together with automation, with frequent, predictable changes and less toil. That is a useful capability to assess, but release counts alone do not measure it: a team may already release quickly or may have less demand for changes. Evaluate the split against the problem it was meant to solve, using agreed measures such as change lead time, deployment failures, or recurring manual work under comparable conditions.
State and instance replacement
A process is easier to replace when required state survives outside it. The Twelve-Factor App recommends stateless application processes with persistent data in backing services. It also permits memory and local files as a temporary cache within one transaction or execution. Losing a rebuildable cache differs from losing the only copy of a completed business operation.
The risk appears when a later request or job depends on that local copy. It may run on another worker, and a restart or relocation may remove the file. Downloading a file, processing it within the same execution, and durably saving the result fits this model; handing a local path to a separately scheduled follow-up job does not establish that the next worker can read it.
Memory-only sessions also constrain routing. A load balancer can use session affinity to keep each user on one instance, as AWS Application Load Balancer documents, so adding instances is possible. But that does not transfer a session when its instance fails or is removed, and existing sessions may distribute unevenly. Twelve-Factor recommends external session storage to avoid that routing dependency.
Elastic scaling, rolling deployment, and recovery all benefit from replaceable processes, but they have additional requirements. Twelve-Factorโs disposability guidance addresses starting promptly, finishing or returning in-flight work, and handling sudden termination. Deployments also need compatible versions during the transition. State belongs somewhere with an explicit durability and recovery contract; moving it out of an application process does not remove that responsibility.
Four kinds of coupling, and which ones a split removes
“Loosely coupled” is doing the work in the definition, so it is worth being specific about what can be coupled. Deployment, data, temporal, and semantic dependencies respond differently to a process boundary. Separate processes make independent deployment possible, but compatible contracts are still required; the other dependencies need their own design.
| Coupling | What it looks like | Does splitting into services fix it? |
|---|---|---|
| Deployment | You cannot release A without releasing B | It enables separate releases if supported API, event, and data contracts remain compatible |
| Data | Two components read or write the same tables | No. Changes can affect both; independent releases need compatible schemas and consumer contracts |
| Temporal | A cannot serve a request unless B is available now | No. A required remote call adds network failure and latency; fallback or decoupling needs separate design |
| Semantic | Both depend on the same meaning of active customer or order complete | No. Changing the meaning still requires agreement, and now it is a negotiation between teams |
The data row concerns the shared model, not merely a shared machine. When two services access the same tables, a storage change must account for both consumers. Some changes remain compatible; others need migration and a coordinated rollout. Microsoftโs microservices guidance recommends private service data stores to reduce this direct coupling. A service can then change its internal schema while maintaining the API or events it promises to others.
Separate schemas with credentials restricted to each serviceโs own tables remove one form of direct data access. Independent deployment still requires compatible API and event contracts, including their meaning, across supported versions. On one database server the services may also share availability and capacity limits. Inspect both the tables each service can access and the contracts through which other services depend on its data.
The temporal row introduces another failure path. A local call does not guarantee that its effects are all-or-nothing: a function that commits a row, writes a file, and then throws leaves the first two effects behind, and a process killed midway can do the same. Atomicity comes from a transaction or an equivalent contract.
An ordinary synchronous call in a running process normally returns a result or propagates an exception directly. A process crash leaves no caller to observe that exception, and asynchronous work in the same process can continue after its caller stops waiting; Pythonโs Future.result timeout is one example. A network adds independently failing request and response paths, so the callee may finish while the caller receives no answer. Partial failure handling must preserve that uncertainty. If the operation is retried, its effect must be safe to repeat under the receiverโs idempotency or equivalent duplicate-recognition contract.
Where the boundary belongs
A useful boundary makes responsibilities and dependencies explicit. Data ownership, reasons to change, and the teamโs ability to operate the result provide a starting point for evaluating it. They need to be considered together with the required consistency and reliability.
- Data ownership. Identify the component responsible for accepting changes to each authoritative record, and make other components use its agreed interface. Unclear or competing write authority calls for revisiting the contract and possibly the boundary. Naming an accountable owner also involves governance, as described in data ownership roles.
- A shared reason to change. Functions that repeatedly change together for the same business reason are candidates to keep together. Splitting closely related behavior can increase cross-service coordination, although not every change to a business capability must affect all its services.
- A team that can own it. Assign responsibility for maintenance, incidents, and change decisions. One team can own several services, provided it has the capacity and knowledge to operate them. A missing owner is a specific gap to resolve.
A bounded context identifies the scope within which a domain model and its vocabulary remain consistent, as Martin Fowler explains. Assigning responsibility helps maintain that model, but the context is not defined by having exactly one team or deployable. Microsoftโs boundary-design guidance considers several service candidates within one context and then checks cohesion, consistency, and operating requirements. When contexts exchange data, make the mapping and any deliberately shared meaning explicit.
When a question spans independently owned data, the read contract must explain how those parts fit together. Two uncoordinated API calls or a federated query may combine states that do not belong to one consistent view. A read model can instead publish compatible versions together, or a supported coordination protocol can provide a stronger guarantee. Merely naming a read model or assigning ownership supplies neither: define the source coverage, version relationship, and reading convention the answer requires.
Choose whether temporary disagreement is acceptable before choosing how to propagate changes. Eventual consistency requires a mechanism that eventually delivers the relevant changes and resolves conflicts under its stated failure assumptions. If a fire-and-forget event is lost and no replay or reconstruction recovers it, the copy can remain wrong indefinitely. Reconciliation can detect such gaps, but repair still needs retained evidence and a working path. A service boundary alone guarantees neither convergence nor the business acceptability of stale data.
The organization is already shaping the design
Conwayโs law relates a systemโs structure to the communication structure of the organization designing it. In his 1968 paper, How Do Committees Invent?, Melvin Conway also allows one design group to produce several subsystems. The relationship does not prescribe one service per team.
The relevant structure includes how people actually exchange information and reach agreements. Reporting lines and time zones can influence those paths, but an org chart alone cannot establish where interfaces will fall or whether they fit the domain. Look for handoffs that delay decisions and for agreements that teams struggle to maintain.
Two practical readings follow, and both are more useful than the slogan.
- Investigate mismatches between ownership and dependencies. Begin with the service list and responsible teams, then examine actual changes: which decisions waited, which contracts caused rework, and who could resolve them? Shared ownership or cross-team work may increase coordination costs, but those arrangements alone do not prove the cause of a delay.
- Consider changes to communication as part of design. Clearer decision rights, regular collaboration, or a reorganization may help teams maintain the intended boundaries. Their cost and lead time depend on the organization. Conwayโs emphasis on flexibility supports revisiting the arrangement as the design improves, rather than assuming that a reorganization guarantees a particular architecture.
Another companyโs topology is useful evidence about its choices. Before adopting it, ask whether the domain, operating scale, and communication arrangements that supported those boundaries also exist in your organization. Copying a topology can import coordination demands that your teams are not prepared to meet.
Modular monolith or microservices
The choice is usually presented as a spectrum of maturity, which is misleading. A modular monolith โ one deployable unit with enforced internal module boundaries and separate data ownership per module โ gets most of the design benefit and defers the operational cost.
| Modular monolith | Separate services | |
|---|---|---|
| Boundaries | Module visibility, dependency checks, and data access rules | Explicit remote interfaces; data permissions and contract rules still need enforcement |
| Calls between modules | Ordinary synchronous calls return or raise directly; crashes and async work need separate handling | Network latency, timeouts, and possible response loss after completion |
| Deployment | One deployable is released together | Separate releases when supported API, event, and data contracts remain compatible |
| Local development and test | One application process; test cost still depends on data and external dependencies | More processes and dependencies to arrange; cost and stability depend on the test design |
| Scaling | The whole unit scales together | Per service, which matters when one part is far hotter than the rest |
| Failure isolation | Process crashes are shared; resource pools can contain some narrower failures | Process failures can be contained; shared resources and dependencies can still affect several services |
Separate services make independent deployment, scaling, and process-level fault containment possible. User-visible failure isolation needs more: two asynchronous consumers can still fail together when their shared broker is unavailable, or miss deadlines when a shared pool is exhausted. Microsoftโs Bulkhead pattern separates resources such as connection pools and queues around the impact to be contained. Check the remaining dependencies and recovery behavior against the reliability target.
Reasons to evaluate a separate service include a very different scaling profile, a reliability target that requires stronger isolation, a release cadence that cannot be shared, or a runtime the current deployment cannot reasonably host. Each needs a cost and feasibility assessment. An unspecified future need does not justify the extra operating work by itself.
The strategy that survives contact with reality is to enforce boundaries early and extract late: get module boundaries and data ownership right inside one deployable, then pull out the pieces that demonstrate a need. Extraction from a well-modularized monolith is a manageable project; extraction from a tangled one is the same work you were avoiding, which is why the modularization is the part that cannot be skipped.
What separation charges you
Four costs arrive with a distributed topology regardless of how well the boundaries are drawn. They are worth naming in advance because they are usually discovered one by one, each as a surprise.
- Operations multiplies. Each service needs a pipeline, a deployment, alerts, an on-call owner, dependency updates, and a patch path. Eleven services is eleven of those, and the platform work that makes it bearable is the reason platform engineering exists as a function.
- Observability stops being optional. A request crossing seven processes cannot be debugged from one log file. Correlated logs and request tracing move from nice-to-have to prerequisite, and retrofitting them during an incident is the expensive way to learn this.
- Testing changes shape. Tests spanning services have more runtime dependencies to arrange. Their speed and stability depend on environment control, data, and scope. Keep fast unit and component tests, verify consumer contracts, and use selected integration and end-to-end tests for behavior that only the assembled system can demonstrate.
- Contract changes still need coordination. A change affecting several services may need a compatibility window, a release order, and migration ownership. Lewis and Fowlerโs discussion of microservices explicitly allows this. Repeated lockstep changes are a reason to examine boundaries; a planned evolution of a public contract is not by itself evidence that the boundary is wrong.
These costs recur, so include them in total cost of ownership as well as the migration estimate. Service count relative to team count is a prompt to investigate, not proof of missing ownership. Check each serviceโs responsible team, maintenance capacity, incident coverage, and change workload. A team can support several services well, or struggle with one.
Deciding, in order
- Name the problem you are solving. Slow releases, a component that cannot scale, a team blocked by another team, a reliability requirement. “Modernization” is not one of these, and a decomposition without a named problem cannot be evaluated later.
- Find the contexts. Differences in business vocabulary help reveal distinct models. Confirm their scope and relationships, then use cohesion, consistency, and operating needs to choose deployment boundaries.
- Assign data ownership and define the authorized write paths. If several contexts need to influence one record, decide which component accepts changes and how the others request them. Revisit ambiguous ownership rather than assuming each context must equal one service.
- Enforce the boundaries in code first. Cheaper to move, and the modularization is what makes later extraction feasible.
- Extract only what has a reason, and write down what you expected to improve โ a release frequency, a latency, a cost. An architecture decision record with that expectation in it is what lets the next team tell whether the split worked.
Keeping boundaries inside one deployment while the domain is still being understood leaves more changes under one release and test cycle. Once interfaces are independently deployed, moving responsibilities also requires consumer compatibility and migration planning. That is the reason to establish useful module boundaries early and extract when a concrete need outweighs those additional costs.
References
- CNCF โ Cloud Native Definition v1.1
- The Twelve-Factor App โ Processes
- The Twelve-Factor App โ Disposability
- AWS โ Application Load Balancer target group attributes
- Microsoft โ Data considerations for microservices
- Python โ concurrent.futures
- Martin Fowler โ Bounded Context
- Microsoft โ Identify microservice boundaries
- Melvin E. Conway โ How Do Committees Invent? (1968)
- Microsoft โ Bulkhead pattern
- James Lewis & Martin Fowler โ Microservices
Discover more from Insightful Data Lab
Subscribe to get the latest posts sent to your email.
