APIs & Microservices

Microservices Architecture

Service Boundaries, Data Ownership, and Decomposition

On this page

Microservices architecture structures a system as a set of independently deployable services, each organized around a business capability and owning the data it manages. The style buys independent change and scaling at the price of distribution: network failure, latency, partial failure, operational load and weaker consistency.

This paper sets out how to decide whether that trade is worth making and how to make it well. It derives service boundaries from the domain with the strategic patterns of domain-driven design, explains data ownership and the patterns for querying and keeping data consistent across services, compares synchronous and asynchronous communication, and describes how to decompose a monolith incrementally with the strangler fig approach and staged database separation. Cross-cutting concerns, team ownership, lightweight governance through decision records and fitness functions, pitfalls, metrics and a staged path complete the picture.

Written for Enterprise and solution architects, engineering managers, senior engineers and platform leads deciding whether and how to adopt or restructure service-based architectures.

Executive summary

Microservices are independently deployable services organized around business capabilities, each owning the data it manages. The promise is organizational: many teams changing and releasing their parts of a system without coordinating every release. The price is distribution: in-process calls become network requests that can fail or half-succeed, and data that once changed in one transaction is spread across stores that can only be eventually consistent. Adopting the style for its reputation rather than for independent change pays the price without collecting the benefit.

This paper takes the position that boundaries, not technology, decide whether microservices succeed. Boundaries should follow the business domain, discovered through the bounded contexts of domain-driven design and through event storming, sized so that one team can own each service with its data, and tested against one question: do the things that change together live together? A modular monolith with the same boundaries is often the right first step, because boundaries are cheap to move inside one deployable unit and expensive to move between services.

  • Each service owns its data exclusively; shared databases recreate the coupling that services were meant to remove.
  • Consistency across services comes from sagas with compensating actions, the transactional outbox and idempotent consumers; a need for atomicity across services signals a wrong boundary.
  • Asynchronous events are preferred wherever the caller does not need an immediate answer; synchronous chains multiply latency and couple availability.
  • A monolith is decomposed incrementally, capability by capability, behind a strangler fig facade, and progress is measured by deployment independence rather than by service count.
  • Every service needs an owning team on call, a golden path, a catalog entry and lightweight governance through decision records and fitness functions.

The paper closes with recurring pitfalls, metrics that measure independence rather than service counts, guidance on merging services back, and a staged path from a modular monolith to continuously evolving services.

What microservices are and are not

A microservices architecture bets that the cost of distributing a system across a network is repaid by the ability of many teams to change, deploy and scale their parts of it independently.

The term entered wide use after James Lewis and Martin Fowler described the style in an influential article: a suite of small services, each running in its own process and communicating through lightweight mechanisms, built around business capabilities, independently deployable by fully automated machinery, and with decentralized control over languages and data. Sam Newman's shorter formulation keeps the essentials: independently releasable services modeled around a business domain.

Three properties carry most of the weight. A service is independently deployable: one that cannot be released without a coordinated change elsewhere is not a microservice, however little code it contains. It is organized around a business capability such as pricing or fulfillment, rather than around a technical layer such as data access. And it owns its data, exposing it only through its application programming interface (API) and its events.

The cost of distribution

Every benefit of the style is paid for in distribution. An in-process call becomes a network request that can time out or fail; partial failure appears when an operation succeeds in one service and fails in another; and latency accumulates across hops. The fallacies of distributed computing, a list that originated with engineers at Sun Microsystems, name the assumptions that in-process designs can make safely and distributed ones cannot: that the network is reliable, that latency is zero and that bandwidth is infinite, among others. Operational load multiplies, because each service needs a pipeline, monitoring, patching and someone on call. Data consistency weakens, because a transaction that once spanned three tables now spans three services that no database will commit atomically.

The modular monolith

A modular monolith is a single deployable unit divided into modules with explicit, enforced boundaries: each module exposes an interface, keeps its data private and reaches other modules only through their interfaces, in process. It delivers most of the design discipline of microservices without a network between the parts, and its boundaries can be checked automatically in the build.

It is often the right first step and, for systems owned by one or two teams, the destination. It stops being sufficient when the deployable unit itself becomes the constraint: many teams queue to release through one pipeline, one capability needs a different scaling profile, or a failure in one module must no longer be able to take the whole system down.

Prerequisites

Four capabilities must exist before the first service is extracted. Without them, each extraction adds operational debt faster than it removes coupling.

Boundaries from the domain

Boundaries are the most consequential decision in a service-based architecture. A technology choice can be reversed inside one service, but a boundary, once data and consumers have accumulated on both sides, is expensive to move. Domain-driven design (DDD), introduced by Eric Evans in his book of that name, supplies the vocabulary for finding boundaries in the business domain.

Bounded contexts and ubiquitous language

A bounded context is an explicit boundary within which one domain model applies and every term has one meaning. Its ubiquitous language is the vocabulary shared by domain experts, the model and the code. The classic signal of a boundary is a word that changes meaning. A product in the catalog has descriptions, images and categories; in fulfillment it has weight, dimensions and a storage location. One model for both yields a bloated entity that nobody owns; two contexts yield two candidate services, linked by a product identifier.

Context maps

A context map records the relationships between bounded contexts and the direction of influence: the upstream context's model shapes what the downstream context receives, and the relationship patterns of DDD describe how each side copes. Each pattern is a decision with a cost. The most common are summarized in the table below. The full catalog also includes partnership, for two teams that succeed or fail together and plan jointly; separate ways, for contexts better left unintegrated; and the big ball of mud, a reminder to contain legacy systems whose models have no clear boundaries.

Table 1Common context map patterns
PatternAdopted byWhat it meansCost or risk
Customer-supplierBoth sidesThe upstream plans for downstream needs; the downstream has a voice in prioritiesNeeds a working relationship and joint planning
ConformistDownstreamThe downstream adopts the upstream model as it isCheap, but tied to decisions it cannot control
Anticorruption layerDownstreamThe downstream translates the upstream model into its own at the boundaryA translation layer to build and maintain; essential against legacy and vendor systems
Open host service and published languageUpstreamOne protocol with a documented interchange schema serves all consumersThe upstream must version and support it like a product
Shared kernelBoth sidesTwo contexts share a small, agreed subset of model and codeEvery change to the kernel must be coordinated

The figure below shows an illustrative map around an order management context. Three upstream contexts shape what it receives, an external payment provider is kept at arm's length, and two downstream contexts react to the events it publishes.

Order management

The context being mapped

  • Catalog

    Upstream; exposes an open host service

  • Pricing

    Upstream; shares prices in a published language

  • Customer identity

    Upstream; order management conforms to its model

  • Payment provider

    External upstream; isolated behind an anticorruption layer

  • Fulfillment

    Downstream; consumes order events

  • Notifications

    Downstream; reacts to order events

Figure 1An illustrative context map around an order management context

Event storming

Event storming, a workshop format introduced by Alberto Brandolini, has domain experts and engineers lay out a business process as a timeline of domain events, then add the commands that cause them, the policies that react to them and the aggregates that enforce consistency. Clusters of events that share a language, and the pivotal events where the language changes, reveal candidate bounded contexts. Its outputs are candidates to validate against data ownership and change patterns, not final boundaries.

Sizing heuristics

No formula gives the right size of a service. The heuristics in the table below are questions to ask of every candidate boundary, with the signal that appears when the answer was wrong. They point one way: a service per bounded context rather than per entity, larger at first than instinct suggests, and split later on evidence of divergent change, scale or ownership.

Table 2Boundary heuristics and the signals of a wrong boundary
HeuristicQuestion to askSignal of a wrong boundary
Team ownershipCan one team own, change and run it?Several teams queue to change one service
Cohesive dataDoes it own the data its operations need?Most operations start by fetching data from elsewhere
Rate of changeDo its parts change for the same reasons?Every release touches only one corner of it
Transactional scopeDo operations that must be atomic stay inside it?Invariants need two services to commit together
Failure isolationCan it degrade without taking neighbors down?One outage cascades through its callers

Data ownership

The defining data rule of the style is database per service: each service is the only writer and the only direct reader of its data store, and other services obtain the data through its API, its events, or read models derived from those events. At first the store may be a schema in a shared database engine; the rule concerns access, not hardware.

A shared database defeats the purpose. The schema becomes a hidden contract that every service depends on, so every change must be coordinated; load from one service degrades the others; and deployment coupling returns through the database.

A service that lets another service read its tables has published its schema as an API, without the versioning, the contract or the right to change it.

Duplication and the source of truth

In a single database, normalization makes each fact live once. Across services, deliberate duplication is normal: the order service keeps the customer's name and delivery address as they were when the order was placed. The rule is not that nothing is duplicated, but that every fact has one owning service and every copy knows where it came from and how it is refreshed, usually through events.

Reference data such as country codes and status lists is the common special case. The choice is made per list, by rate of change and the consequence of disagreement: a copy per service for near-static standards such as ISO 3166 country codes, a versioned library where lists must agree within a release, and a reference data service or change stream where they change often.

Querying across services

The hardest consequence of data ownership is the query that spans several owners, such as open orders for customers with unpaid invoices. No service can answer it alone, and the patterns in the table below trade freshness, complexity and load differently.

Table 3Cross-service data access patterns and their trade-offs
PatternHow it worksStrengthsWeaknesses and fit
API compositionA composer calls each owner and joins the results in memorySimple; always current; no extra storeBound by the slowest service; poor for large joins
Event-fed read modelA query service maintains a denormalized view from eventsFast, tailored queries; owners unburdenedEventually consistent; a projection to operate
Change data captureA connector streams database changes to an analytical storeLow impact on the source; feeds reportingExposes internal schemas unless mapped; lag
Local replica by eventsEach consumer keeps a copy of the fields it needsLocal joins; works through outagesCopies to maintain; stale windows

Command Query Responsibility Segregation (CQRS), a pattern described by Greg Young, separates the model that handles commands from read models maintained from the owning services' events. It is the standard answer to cross-service queries that must be fast, at the price of eventual consistency, a projection to operate and a second place where business meaning is encoded. It belongs on the specific queries and bounded contexts that need it, not across a whole system by default.

Analytics is the extreme case: analysts need joins across every domain and history over time. Pointing business intelligence tools at service databases recreates the shared database. Instead, each service publishes data to the analytical platform under a contract, as events, as change data capture with an explicit schema mapping, or as periodic exports, and the analytical store does the joining.

Consistency and transactions

Inside a service, operations use local transactions, and the DDD aggregate, a cluster of objects changed together under one set of invariants, is the unit of consistency, one aggregate per transaction. Across services there is no atomic commit. Two-phase commit ties every participant's availability to a coordinator and needs resource managers that support the protocol, which HTTP APIs and many message brokers do not, so microservice systems reach for sagas instead.

The saga pattern

Hector Garcia-Molina and Kenneth Salem described the saga in a database research paper: a long-lived transaction written as a sequence of smaller transactions, each committed on its own, with a compensating transaction for each so that completed steps can be semantically undone when a later step fails. Across services each step is a local transaction, and compensation is a business action, such as releasing reserved stock or refunding a payment, not a database rollback. Some actions, such as a sent email, cannot be compensated, so a well-designed saga orders its steps: compensatable steps first, then the pivot step after which the saga is committed to completing, then steps that can only be retried until they succeed.

Sagas are coordinated in one of two ways. In choreography, each service reacts to events from the others and emits its own; there is no coordinator. In orchestration, a dedicated orchestrator sends each participant a command, waits for the reply and decides the next step or the compensation.

Table 4Saga choreography versus orchestration
DimensionChoreographyOrchestration
CoordinationImplicit, through the events each service emitsExplicit, through orchestrator commands
CouplingLoose; participants know only event schemasParticipants depend on the orchestrator
Visibility of stateSpread across services; hard to seeHeld in one place; easy to query
Failure handlingEach service must know which compensation to triggerThe orchestrator drives compensation in order
Best fitFew steps; simple, stable flowsMany steps; long-running or audited processes

The transactional outbox and idempotent consumers

A saga step changes local state and announces the change. Doing the two as separate writes is the dual-write problem: a crash between them leaves the state changed and the event unpublished, or an event published for a change that was rolled back. The transactional outbox writes the outgoing event into an outbox table in the same local transaction as the state change; a relay, polling the table or reading the database's transaction log, then publishes it and marks it sent. Publication becomes at-least-once instead of maybe-never.

BEGIN;
UPDATE orders SET status = 'CONFIRMED' WHERE order_id = :orderId;
INSERT INTO outbox (event_id, aggregate_id, event_type, payload)
  VALUES (:eventId, :orderId, 'OrderConfirmed', :payload);
COMMIT;
-- A relay publishes unsent outbox rows to the broker, then marks them as sent.

At-least-once delivery means every consumer will eventually see a duplicate. The idempotent consumer records processed message identifiers in the same transaction as its own state change and ignores repeats, or its handlers are written so that applying a message twice changes nothing. Brokers typically guarantee order only within a partition or for one key, so consumers also carry version numbers to detect and discard stale updates.

BEGIN;
INSERT INTO processed_messages (message_id) VALUES (:messageId);  -- rejected if already seen
UPDATE stock SET reserved = reserved + :quantity WHERE sku = :sku;
COMMIT;

Living with eventual consistency

Eventual consistency is a product and process question before it is a technical one. After placing an order the customer sees that the order was received, not that it shipped; where users must see the result of their own action at once, the read goes to the write model. Business processes need an explicit rule for the inconsistency window, such as a reservation with an expiry or an overbooking policy, and the lag itself is measured, bounded by an objective and alerted on.

If a business invariant must hold at every instant across two pieces of data, as with an account balance that may never go negative, those pieces belong in one aggregate in one service. Sagas for trivial operations, compensations that users notice and recurring reconciliation breaks all signal that a boundary cuts through a transaction. The remedy is to move the boundary, not to build a cleverer saga.

Communication between services

Services communicate in two families. Synchronous request-response, over HTTP with representational state transfer (REST) conventions or over gRPC, makes the caller wait for an answer. Asynchronous messaging through a broker lets the caller continue and the receiver act later. REST suits resource-oriented interfaces that any client can consume, gRPC suits typed, high-volume calls between services, and GraphQL suits client-facing aggregation.

Temporal coupling and call chains

A synchronous call couples the caller to the callee in time: both must be up, and the caller's latency includes the callee's. Across a chain of calls, availability multiplies and latency adds up; five services in a chain, each available 99.9 percent of the time, are together available about 99.5 percent of the time. A slow service at the end of a chain holds threads and connections open in every service before it, which is how cascading failures start. Timeouts, retries with budgets, circuit breakers and bulkheads contain the damage. The structural remedy is a shorter chain: local copies of frequently read data, events in place of calls, or a redrawn boundary.

Need for a guaranteed, coordinated outcome

Orchestrated saga

Multi-step process with compensation

Keep it in one service

Local transaction in one aggregate

Publish events

Consumers update their own state

Synchronous query

API or read model, with a timeout and a fallback

Need for an immediate answer

Figure 2Choosing an interaction style by the need for an immediate answer and for a coordinated outcome

The figure above summarizes the choice. Where the caller needs neither an immediate answer nor a guaranteed joint outcome, it publishes an event and lets consumers update their own state. Where it needs an answer now, it queries synchronously, ideally against a read model, with a timeout and a fallback. Where a multi-step outcome must be guaranteed but need not be immediate, an orchestrated saga provides it. Where the outcome must be both immediate and guaranteed, the work belongs inside one service, in one local transaction.

Gateways and backends for frontends

An API gateway is the single entry point for external clients, routing requests to services and handling edge concerns once: authentication, rate limiting and protocol translation. Its risk is scope creep, with business logic migrating into it until it becomes the central bus the style set out to avoid. The backend for frontend (BFF) pattern answers the composition need differently: each client type gets its own thin aggregation layer, owned by the team that builds that client.

Discovery and the service mesh

Service instances come and go, so callers need service discovery: a registry, or the orchestrator's internal Domain Name System (DNS), maps a logical name to the current endpoints. A service mesh goes further. It places a proxy beside each instance (or, in sidecarless designs, on each node) and configures all the proxies centrally to provide mutual Transport Layer Security (TLS), retries, timeouts, circuit breaking, traffic shaping and uniform telemetry without changes to application code; Istio and Linkerd are examples of the category.

The mesh is itself a distributed system to operate, with extra latency per hop and failures that are hard to debug. It pays off where many services in several languages need uniform policy, not for a dozen services that a shared library and a gateway can serve.

Contracts and compatibility

Whatever the transport, each interface is a contract: a schema, its semantics, an error model such as problem details for HTTP APIs (RFC 9457), and a versioning policy. Contracts are designed before they are implemented, kept compatible by additive change and consumer-driven contract tests, and retired through published deprecation windows. Event schemas are governed the same way, usually through a schema registry that rejects incompatible changes before they reach consumers.

Decomposing a monolith

Most service estates come from decomposition, and the big-bang rewrite remains the most reliable way to fail at it: the monolith keeps changing while the rewrite catches up, and value arrives only at the end, if at all. Incremental decomposition delivers value with each step and keeps a path back.

Strangler fig and related patterns

The strangler fig approach was named by Martin Fowler after the fig that germinates in a host tree, grows around it and eventually stands in its place. Applied to software, a new system grows around the old one until the old one can be retired. In its common form, described by Sam Newman among others, a facade or proxy is placed in front of the monolith and capabilities move behind it one at a time. The approach needs an entry point that can be intercepted, so the first task of any extraction is finding the seam, in Michael Feathers' term a place where behavior can be changed without editing the code in that place.

Where a capability has no such entry point, branch by abstraction, a technique named by Paul Hammant, introduces an interface over the existing implementation, moves all callers onto it, builds the new implementation behind the same interface and switches with a feature flag. A parallel run executes the old and new implementations for the same requests and compares the results before switching, taking care that side effects such as payments and emails happen only once.

  1. Find the seam

    An entry point that can be intercepted

  2. Insert the facade

    A proxy in front; nothing changes yet

  3. Build the service

    The capability and its data, behind a contract

  4. Verify in parallel

    Shadow traffic; compare results; fix gaps

  5. Switch traffic

    Route by flag; keep rollback ready

  6. Retire old code

    Delete code and tables; update the catalog

Figure 3Strangler fig migration of one capability
Table 5Decomposition patterns and their risks
PatternWhat it doesUse it whenRisk to manage
Strangler figA facade routes calls progressively to new servicesThe capability has an interceptable entry pointThe facade becomes a permanent logic layer
Branch by abstractionAn interface over the old code, with the new implementation behind itThe capability is deep inside the codebaseThe abstraction lingers after migration
Database view or wrapperMonolith data exposed through a view or a service, not tablesConsumers must be weaned before tables moveThe view becomes a permanent shared schema
Split table, switch writersSeparate the tables, synchronize, move the writes, drop the old tablesData must move with the capabilitySilent divergence during dual writes

Extracting a capability with its data

Extract by capability, and take the data along: a service that still reads the monolith's tables has moved code, not responsibility. Good first candidates have their own tables with few foreign keys into the rest, change often or need isolation, and carry low business risk. The database follows in reversible steps, each measured for divergence.

  1. Map the data. Identify the tables the capability owns and the keys that will cross the new boundary, and split tables that serve two contexts.
  2. Cut the foreign keys. Replace cross-boundary foreign keys with identifiers resolved through the owning service; application checks replace database constraints.
  3. Wrap what remains. Give remaining consumers a view or a wrapping service over the monolith's data, never new direct table access.
  4. Move the writer. Load the new store, keep it current by change data capture or dual writes while comparing the two, then make the new service the sole writer and drop the old tables.

Sequencing and measuring progress

Sequence by value and risk: an easy, low-risk capability first to prove the platform, then the capabilities with the most change or the strongest need for independent scaling, and the entangled core last. Measure progress by the share of changes deployed without touching the monolith, lead time in extracted capabilities, incidents attributable to the migration, and remaining cross-boundary table reads, which should fall to zero.

Cross-cutting concerns

Some concerns belong to every service, and inconsistent handling causes the failures that are hardest to find. For each, the first decision is where it lives: in a shared library, in a sidecar beside each instance, or in a platform service consumed over the network.

Shared library

Fastest in a single-language estate; it drifts as services pin versions, and a fix means redeploying everything.

Sidecar

A companion process for networking, security and telemetry; language-neutral and uniform, at the cost of resources per instance.

Platform service

A central capability such as an identity provider or secrets manager; one place to operate, and a dependency for every service.

Identity, configuration and secrets

Two identities travel with every call. The service's own identity rests on workload certificates and mutual TLS, with the Secure Production Identity Framework for Everyone (SPIFFE), a Cloud Native Computing Foundation (CNCF) project, as one open standard for naming and attesting workloads. The end user's identity travels as an access token, typically a JSON Web Token (JWT, RFC 7519), passed or exchanged along the chain so that each service authorizes the actual user rather than trusting its caller. OAuth 2.0 token exchange (RFC 8693) issues a narrower token for each downstream audience instead of forwarding the original token everywhere.

Configuration stays outside the build artifact, so that one artifact is promoted through environments and configured at deployment, with feature flags separating deployment from release. Secrets live in a secrets manager with short-lived credentials, rotation and auditing, never in repositories or images.

Tracing, schema evolution and tenancy

A trace identifier created at the edge and propagated through every call and message, using the World Wide Web Consortium (W3C) Trace Context headers, makes a request debuggable across services. OpenTelemetry, a CNCF project, provides vendor-neutral instrumentation, and logs carry the trace identifier so that logs and traces can be joined.

Interfaces and events evolve by expand and contract: add the new field or version beside the old one, migrate consumers, then remove the old one; never change a type in place. A schema registry enforces this for events, and consumer-driven contract tests do so for APIs, while a service's internal schema remains its own business as long as nothing outside reads it.

In a multi-tenant system the tenant identifier travels with the user identity, and each service applies the agreed isolation model, whether a tenant key with row-level enforcement or a schema or database per tenant. Quotas are tenant-aware, and every log line and trace carries the tenant.

Golden paths

A golden path, also called a paved road, is the platform team's supported way to create and run a service: a template repository with the pipeline, telemetry, security baseline and deployment manifests already in place. Teams may leave the path, but then they own the consequences. The golden path is a product, measured by adoption and by the time from an empty repository to the first production deployment, and it is the main lever a platform team has for reducing the cognitive load on the teams that build services.

Teams and ownership

Melvin Conway observed, in a paper titled How Do Committees Invent?, that any organization that designs a system will produce a design whose structure copies the organization's communication structure; Fred Brooks later named the observation Conway's law. For service architectures this is a design constraint: a boundary that cuts across a team generates coordination overhead until the team quietly merges the services, and a service shared by several teams drifts toward the shape of their negotiations.

The inverse Conway maneuver, a term generally credited to Jonny LeRoy and Matt Simons and discussed at length in Team Topologies, turns the law into a tool: shape the teams around the boundaries the architecture needs, which should follow bounded contexts, and the system will tend to take the shape of the teams.

Team Topologies

Matthew Skelton and Manuel Pais describe four team types and three interaction modes. A stream-aligned team owns a flow of change for a business domain; a platform team provides internal services that reduce other teams' cognitive load; an enabling team helps other teams acquire a capability; and a complicated-subsystem team owns a part that needs specialist knowledge. Teams interact through collaboration, X-as-a-Service consumption or facilitation. Mapped onto services, each bounded context belongs to one stream-aligned team, the platform team runs the golden path, and enabling teams help with domain modeling. A team can own only as many services as it can understand and operate well.

The team that builds a service deploys it, watches it and is on call for it, the practice Werner Vogels summarized as "you build it, you run it". The closed loop makes operability a design concern rather than a hand-off. Transferring a service to another team is a formal handover with a catalog update, never a quiet orphaning.

Table 6Responsibilities in a service-based organization
ResponsibilityStream-aligned teamPlatform teamArchitecture function
Service boundariesProposes and evolves boundaries within its domainAdvises on platform fitFacilitates context mapping; arbitrates disputes
Contracts and APIsDesigns, versions and supports themProvides the gateway, registry and toolingSets standards; runs the design review
Data ownershipOwns its stores and published dataProvides managed data servicesKeeps the ownership map of shared concepts
Operations and on-callRuns and is paged for its servicesRuns and is paged for the platformReviews incident patterns across services
Decisions and catalogRecords service decisions; keeps its entry currentRecords platform decisions; operates the catalogRecords cross-cutting decisions; audits coverage

Service catalog and lightweight governance

A service catalog, part of an internal developer portal such as Backstage, lists every service with its owning team, on-call rotation, criticality tier, dependencies, contracts, service level objectives and lifecycle status. It stays true only when it is populated from metadata in each service's repository and from the platform itself, and a service without an entry should not be deployable.

Two artifacts govern without committees. Architecture decision records (ADRs), a format proposed by Michael Nygard, capture the context, decision and consequences of each significant choice in a short document kept with the code. Fitness functions, described by Neal Ford, Rebecca Parsons and Patrick Kua in Building Evolutionary Architectures, are automated checks that an architectural property still holds: no service connects to another service's database, every service has an owner, synchronous call depth is bounded. They run in the pipeline and turn principles into gates, leaving design review and API governance to handle what automation cannot judge.

Architecture that is enforced by the pipeline survives reorganizations; architecture that is enforced by a committee does not.

Pitfalls, metrics and the path forward

Failures of microservice adoptions are consistent, and most trace back to boundary and ownership decisions rather than to technology.

Metrics

Metrics should measure independence, not count services. Trends matter more than single values, and the software delivery metrics described in Accelerate by Nicole Forsgren, Jez Humble and Gene Kim (deployment frequency, lead time for changes, change failure rate and time to restore service) apply per service.

  • Deployment independence. The share of production deployments that involve exactly one service; it should rise over time.
  • Lead time per service. Commit to production for each service; outliers point to coupling or missing automation.
  • Coupling signals. Services changed together, synchronous call depth per request, and direct reads of another service's store.
  • Incident blast radius. Services affected per incident and time to restore; failures should stay inside one service.
  • Ownership and load. Services with a named team and a current catalog entry, and services and pages per team.

Merging services back

Boundaries are hypotheses, and some will be wrong. Two services that always change together, a saga wrapped around a simple operation, and data copied in both directions all signal it. Merging is legitimate and usually easier than the original split: combine the codebases behind the existing API facade, merge the stores by reversing the split steps, and record the reasoning in an ADR so that a later team does not split them again for the same reasons.

A staged path

  1. Stage 1
    Modular monolith

    Enforced module boundaries; data per module

  2. Stage 2
    First extractions

    One or two capabilities; pipeline and tracing

  3. Stage 3
    Platform and ownership

    Golden path, catalog, teams own on-call

  4. Stage 4
    Autonomous services

    Events, sagas, read models; independent releases

  5. Stage 5
    Continuous evolution

    Fitness functions; merge and split on evidence

Figure 4A staged path to sustainable microservices

Each stage has an entry condition. The modular monolith establishes boundaries and data ownership per module while delivery automation and observability are built. The first extractions prove the platform on one or two low-risk capabilities. Platform and ownership add the golden path and a catalog with an owner for every service. Autonomous services arrive when teams release independently, coordinating through events, sagas and read models rather than through release calendars. Continuous evolution revisits boundaries on evidence, merging and splitting as fitness functions and coupling signals indicate.

Conclusion

Microservices are an organizational architecture expressed in software. They repay their cost of distribution only where many teams need to change a system independently, and only when boundaries follow the domain, each service owns its data, consistency is designed rather than assumed, and every service has a team and a paved road to production. Where those conditions do not hold, a modular monolith with the same boundaries is the better architecture, and saying so is part of the architect's job.

The practical starting points are modest, and everything else follows from doing them well.

  1. Map the bounded contexts. Draw a context map of the current system that records upstream and downstream relationships and the pattern used for each.
  2. Name owners. Assign an owning team to every deployable unit, and an owner to every shared concept such as the customer.
  3. Remove cross-boundary database access. Replace direct reads of another module's or service's tables with APIs, events or read models, and add a fitness function that keeps them out.
  4. Extract when ready. Extract the first capability only when the pipeline, the telemetry and the team to run it are in place.

Glossary

Anticorruption layer
A translation layer that converts another context's model into the local model at the boundary, isolating the local model from foreign concepts and changes.
Architecture decision record (ADR)
A short document, numbered and kept with the code, that records the context, decision and consequences of a significant architecture choice.
Backend for frontend (BFF)
An edge service dedicated to one client type that composes calls to services into responses shaped for that client.
Bounded context
An explicit boundary within which a particular domain model applies and each term has one meaning.
Context map
A record of the relationships between bounded contexts, including the direction of influence and the integration pattern used for each.
CQRS
Command Query Responsibility Segregation: separating the model that handles writes from models built for reads.
Distributed monolith
A set of services that must be deployed together, share a database or depend on long synchronous chains, carrying the costs of distribution without independence.
Event storming
A workshop technique that maps a business process as a timeline of domain events to discover commands, policies, aggregates and candidate bounded contexts.
Fitness function
An automated, continuously executed check that an architectural property still holds, such as the absence of cross-service database access.
Golden path
A supported, opinionated way to build and run a service, provided by the platform team with templates, pipelines and defaults; also called a paved road.
Idempotent consumer
A message consumer that produces the same outcome whether it receives a message once or several times.
Inverse Conway maneuver
Deliberately shaping team structure and communication to match the architecture an organization wants, rather than letting the existing organization dictate it.
Modular monolith
A single deployable unit whose internals are divided into modules with enforced interfaces and private data.
Saga
A sequence of local transactions across services in which each step has a compensating action that undoes its effect if a later step fails.
Seam
A place in a codebase where behavior can be changed or intercepted without editing the code at that point.
Strangler fig
An incremental migration approach in which a new system grows around an old one, typically behind a facade that routes calls progressively to new services, until the old system can be retired.
Transactional outbox
A pattern that writes outgoing messages to a table in the same local transaction as the state change and relays them to the broker afterward.
Ubiquitous language
The vocabulary shared by domain experts, the model and the code within a bounded context.

References and further reading

  1. Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software, Addison-Wesley.
  2. Vaughn Vernon, Implementing Domain-Driven Design, Addison-Wesley.
  3. Sam Newman, Building Microservices: Designing Fine-Grained Systems, second edition, O'Reilly Media.
  4. Sam Newman, Monolith to Microservices: Evolutionary Patterns to Transform Your Monolith, O'Reilly Media.
  5. James Lewis and Martin Fowler, Microservices: a definition of this new architectural term, martinfowler.com.
  6. Martin Fowler, Strangler Fig Application and CQRS, martinfowler.com.
  7. Chris Richardson, Microservices Patterns, Manning.
  8. Hector Garcia-Molina and Kenneth Salem, Sagas, Proceedings of the ACM SIGMOD International Conference on Management of Data.
  9. Alberto Brandolini, Introducing EventStorming.
  10. Matthew Skelton and Manuel Pais, Team Topologies: Organizing Business and Technology Teams for Fast Flow, IT Revolution Press.
  11. Melvin E. Conway, How Do Committees Invent?, Datamation.
  12. Neal Ford, Rebecca Parsons and Patrick Kua, Building Evolutionary Architectures, O'Reilly Media.
  13. Michael C. Feathers, Working Effectively with Legacy Code, Prentice Hall.
  14. Michael Nygard, Documenting Architecture Decisions.
  15. Nicole Forsgren, Jez Humble and Gene Kim, Accelerate: The Science of Lean Software and DevOps, IT Revolution Press.
  16. Martin Kleppmann, Designing Data-Intensive Applications, O'Reilly Media.

This paper offers general guidance and is not legal, regulatory or professional advice. Summaries of laws, regulations and standards are for orientation only; confirm the obligations that apply to your organization with qualified advisors. Product and vendor names are used for illustration and imply no endorsement.

© ByteCI, LLC. All rights reserved.

Put this into practice

Want help applying this?

ByteCI helps federal and private-sector teams turn guidance like this into working architecture, roadmaps and delivery. Tell us about your situation.