Monoliths and Microservices: Choosing Deployment and Ownership Boundaries
📑 On this page
- A concrete example: a small online shop
- What a monolith is
- Benefits of a monolith
- Limits of a monolith
- What a microservice is
- Benefits of microservices
- Network calls replace function calls
- Data ownership is the difficult boundary
- Transactions become workflows
- Independent deployment requires compatibility
- Operational maturity
- Testing changes
- Fault isolation is not automatic
- Organizational alignment
- The distributed monolith
- When to extract a service
- Knowledge check
- The one idea to remember
Software architecture discussions sometimes treat a monolith as an old mistake and microservices as an automatic upgrade.
That framing ignores the trade.
A monolith packages multiple capabilities into one deployable application; microservices package capabilities into independently deployed networked services.
Microservices can improve independent ownership and scaling. They also replace in-process complexity with distributed-system and operational complexity.
A concrete example: a small online shop
A five-person team operates:
- Product catalog
- Cart
- Checkout
- Customer accounts
- Order history
One well-structured application may let the team:
- Change related features together
- Run tests locally
- Use one transaction for an order
- Deploy one artifact
- Trace one process
Splitting this into twenty services gives the team twenty deployments, service identities, dashboards, versioned contracts, and failure paths.
The smaller code repositories do not reduce total system complexity.
What a monolith is
A monolith is one deployable unit.
It may still contain:
- Modules
- Layers
- Internal APIs
- Background jobs
- Several user interfaces
- One or more databases
Monolith does not mean one giant file or no architecture.
A modular monolith preserves strong internal boundaries while deploying together.
Many problems blamed on monoliths are actually problems of poor modularity, unclear ownership, or unsafe deployment practices.
Benefits of a monolith
A monolith often provides:
- Simple local development
- Direct function calls
- Straightforward transactions
- One deployment pipeline
- Easier end-to-end refactoring
- Fewer network failures
- Centralized observability
- Lower infrastructure cost
For a small or rapidly learning product, these advantages are substantial.
One process can still scale horizontally behind a load balancer, and modules can preserve future extraction options.
Limits of a monolith
As a monolith and organization grow:
- Builds and tests may slow.
- One deployment carries many changes.
- Teams coordinate on shared code.
- A memory leak can affect the whole process.
- One workload may force scaling everything.
- Technology choices become shared.
- Ownership can blur.
These pressures are not guaranteed.
Good tooling, module boundaries, test selection, and deployment automation can extend the useful life of a monolith considerably.
Measure the actual bottleneck before splitting.
What a microservice is
A microservice is an independently deployable service aligned to a business capability or bounded responsibility.
It should normally own:
- Its code
- Its interface
- Its operational lifecycle
- Its relevant data decisions
- A team accountable for it
The word micro does not specify line count.
A service that is too small may create chatty calls and fragmented ownership. A service that is too broad may preserve the same coordination problem behind an HTTP boundary.
Benefits of microservices
Microservices can provide:
- Independent deployment
- Independent scaling
- Clear team ownership
- Fault isolation
- Technology choice within boundaries
- Smaller code context per service
- Security isolation for sensitive capabilities
A video-processing service may scale GPU workers without scaling account management.
A payment service can have stricter access and release controls.
These benefits appear only when the boundaries and operational practices support them.
Network calls replace function calls
An in-process call:
total = calculateTotal(cart)is fast and usually succeeds or fails immediately.
A service call can:
- Time out
- Succeed after the caller times out
- Return a partial error
- Be retried
- Encounter incompatible versions
- Lose network connectivity
Every distributed boundary needs:
- Timeout
- Retry policy
- Idempotency
- Authentication
- Serialization
- Monitoring
- Failure behavior
This is the distributed-system tax.
Data ownership is the difficult boundary
If all services directly modify one shared database, they remain tightly coupled through schema and transactions.
Independent ownership often means each service controls its data and exposes behavior through APIs or events.
This creates challenges:
- Cross-service reporting
- Eventual consistency
- Duplicate data
- Distributed workflows
- Data migration
Do not split code into services while leaving ambiguous data ownership.
Data boundaries are often harder than deployment boundaries.
Transactions become workflows
Inside one database transaction, checkout can atomically create an order and reserve inventory.
Across services, one global transaction may be unavailable or undesirable.
The workflow may:
- Create pending order.
- Reserve inventory.
- Request payment.
- Confirm order.
- Compensate when a step fails.
Users can observe intermediate states.
The design needs idempotency, durable state, retries, and reconciliation.
Microservices make business failure paths explicit, but the implementation is more complex.
Independent deployment requires compatibility
Service A and Service B will not always deploy simultaneously.
Interfaces need:
- Backward-compatible changes
- Versioning policy
- Consumer-aware testing
- Deprecation periods
- Tolerant readers
Database migrations may use expand-and-contract:
- Add new structure compatibly.
- Deploy code that uses both forms.
- Migrate data.
- Remove old structure after all consumers change.
Independent deployment is an engineering capability, not merely separate repositories.
Operational maturity
Microservices multiply:
- Deployments
- Logs
- Metrics
- Traces
- Alerts
- Certificates
- Secrets
- On-call ownership
- Dependency graphs
Teams need:
- Automated delivery
- Service templates
- Central observability
- Reliable infrastructure
- Incident practices
- Cost visibility
- Clear ownership
Without this platform and culture, service autonomy becomes operational fragmentation.
Testing changes
A monolith can run broad tests in one process and database.
Microservices need a layered strategy:
- Unit tests
- Component tests
- Contract tests
- Integration tests
- Focused end-to-end tests
- Production monitoring
Testing every combination of live services is expensive and unstable.
Contract tests verify interface assumptions while each service remains independently testable.
End-to-end tests still matter for critical journeys, but cannot carry the entire confidence burden.
Fault isolation is not automatic
Separate processes can limit memory or crash propagation.
But a failing service can still trigger:
- Retry storms
- Thread exhaustion
- Queue buildup
- Cascading timeouts
- Shared database overload
Use timeouts, concurrency limits, circuit breakers, bulkheads, queues, and graceful degradation.
A network boundary creates the opportunity for isolation, not the guarantee.
Organizational alignment
Microservices work best when a team can own a service end to end:
- Development
- Deployment
- Operations
- Reliability
- Roadmap
If every change still needs approval from several centralized teams, the service boundary may add handoffs rather than autonomy.
Conversely, a large monolith shared by dozens of teams can make coordination expensive.
Architecture and organization shape one another.
The distributed monolith
A distributed monolith has many services but must deploy or change them together.
Signs include:
- Shared database tables
- Synchronous call chains
- Breaking internal APIs
- One release train
- No independent ownership
- Local development requiring every service
It combines microservice operational cost with monolithic coupling.
The remedy may be stronger service boundaries, fewer services, or rejoining components that do not benefit from separation.
When to extract a service
Evidence for extraction includes:
- One capability needs independent scaling.
- A distinct team owns it.
- It has a clear data boundary.
- Security requires isolation.
- Its release rate conflicts with the rest.
- Failure should be contained.
- A stable interface already exists.
Extraction has migration cost.
Start with a narrow boundary, preserve compatibility, move ownership deliberately, and monitor the new failure modes.
Do not begin with the easiest code to move if it creates a meaningless service.
Knowledge check
- Why is a monolith not necessarily poorly structured?
- What new concerns appear when a function call becomes a network call?
- Why is data ownership central to microservice independence?
- What is a distributed monolith?
- Which evidence can justify extracting a service?
The one idea to remember
Monoliths simplify local change and consistency; microservices enable independent deployment, scaling, and ownership by accepting network, data, and operational complexity. Choose the boundary that fits the real organization and problem.