Microservices: Architecture and Decomposition
Definition
Microservices split an application into small services. Each service handles one business job, owns its own data, and can be built and deployed on its own. Services talk to each other over the network.
Why it exists
In one big application, every change ships together and one bug can bring everything down. Small independent services let each team release and scale its own part.
How it works
How to find service boundaries
- Bounded contexts (DDD). A bounded context is a part of the domain where a term has one precise meaning. "Customer" in Billing (payment details) differs from "Customer" in Shipping (addresses). Each context is a candidate service.
- Business capability. Order Management, Inventory, Payments, Notifications.
- Change together, stay together. If two pieces always change in the same release, they likely belong in one service.
- Data ownership. Each service is the single owner of its data; no one else touches its tables.
System design considerations
- Each service owns its data: share APIs and events, never tables
- Every remote call can fail or be slow, so design timeouts, retries and fallbacks from the start
- Budget for the platform: tracing, logging, service discovery and automated delivery
Database per service
Sharing a database couples services through the schema: one team's migration breaks another. The rule is that other services must use the owner's API or consume its events. The consequence is that you lose cross-service joins and ACID transactions, which leads to the patterns in this section:
- Queries across services: API composition or CQRS read models.
- Updates across services: Saga.
- Reliable event publishing: Outbox.
The cross-cutting platform
Each service needs the same supporting capabilities. Most of this section is about them:
| Capability | Page |
|---|---|
| Single entry point, auth, routing | API Gateway |
| Finding instances | Service Discovery |
| Central configuration | Config Server |
| Service-to-service calls | Communication |
| Surviving failure | Resilience4j |
| Seeing what happens | Observability |
When to use it, and when not
Use it when
- Several teams need to ship independently and the domain has clear boundaries
- Parts of the system have very different scaling or availability needs
- You have the platform maturity for CI/CD, observability and on-call
Think twice when
- A small team with one product, where a modular monolith is faster and cheaper
- The boundaries are unclear, which produces a distributed monolith
- You lack monitoring and deployment automation
Monolith versus microservices
| Concern | Modular monolith | Microservices |
|---|---|---|
| Deployment | One unit | Many independent units |
| Calls between modules | In-process, fast, transactional | Network, can fail, latency |
| Data consistency | ACID transactions | Eventual consistency, sagas |
| Team scaling | Merge conflicts, shared release | Teams own services end to end |
| Operational cost | Low | High (discovery, tracing, CI/CD for each) |
| Scaling | Whole app | Per service |
Microservices solve an organisational and scaling problem. If one team owns the whole product, a well-structured modular monolith is often the better answer, and a strong interview answer says so.
Common mistakes
- Splitting by technical layer (a "database service", a "validation service") instead of business capability.
- Too many tiny services (nano-services) created before the domain is understood.
- Synchronous call chains: availability is the product of each service's availability.
- Skipping automation: without CI/CD, tracing and centralised logging, microservices are unmanageable.
Interview questions
1. What are the advantages and disadvantages of microservices?
Advantages: independent deployment and scaling, team autonomy, fault isolation, technology freedom. Disadvantages: distributed-system complexity (latency, partial failure, eventual consistency), operational overhead, harder debugging and testing, data duplication.
2. How do you decide the size of a service?
Not by lines of code. Use bounded contexts and team ownership: a service should be small enough for one team to understand and own, and large enough to complete a business operation without chatty calls to others.
3. How would you migrate a monolith?
Strangler fig: put a gateway in front, extract one capability at a time (starting with a loosely coupled, high-change one), route traffic to the new service, and retire the old code. Avoid a big-bang rewrite.
4. How do services share data?
Through APIs and events, never a shared schema. Duplicated read-only copies (built from events) are acceptable and often necessary.
5. What is a distributed monolith?
Services that must be deployed together, share a database, or call each other synchronously in long chains. You get all the cost of microservices and none of the benefit.
6. What is the CAP theorem's relevance?
During a network partition you choose consistency or availability. Most microservice designs choose availability with eventual consistency and compensate for inconsistencies.
Check yourself
1What is the main danger of splitting services without clear boundaries?
2How should you find service boundaries?
3Why does each microservice own its database?
Key takeaways
- Use microservices for team and scaling reasons, not by default.
- Draw boundaries around bounded contexts and data ownership.
- Database per service means eventual consistency, sagas and events.
- Invest in platform capabilities (discovery, config, tracing) from the start.
Finished this page? Track your progress (saved only in your browser).