Distributed Monolith: When Microservices Are Just a Monolith with Network Latency
3 services, 1 PostgreSQL, shared Redis, one docker-compose — and the illusion of independence. How to spot a distributed monolith in your own architecture, when it's actually useful, and when it's time for real separation.
Distributed Monolith: When Microservices Are Just a Monolith with Network Latency
You split your code into services. You have separate containers. You even have a gateway. So why does deploying one service still break the other?
What is a distributed monolith
A distributed monolith is an architecture that looks like microservices but behaves like a monolith. Services are separated at the code level but remain coupled at the infrastructure, data, or deployment level.
Classic symptoms:
- Shared database – different services read/write to the same PostgreSQL instance
- Shared library without versioning – a change in a common package breaks everyone simultaneously
- One docker-compose – all services are deployed together, even if only one changed
- Synchronous HTTP calls – service A cannot function if service B is unresponsive
- Shared cache – one Redis for everyone, LRU eviction from one service kills another's cache
Sound familiar? That's our architecture. And we believe that right now – it's the right choice.
When a distributed monolith is the right choice
Here's an unpopular opinion: a distributed monolith isn't always a problem. At a certain scale, it's the optimal architecture.
Benefits we get
1. Operational simplicity – One docker compose up brings everything up.
2. Development speed – A shared package means DRY.
3. Transactional integrity – One PostgreSQL = the ability to JOIN across schemas.
4. Debuggability – One docker compose logs shows the entire request flow.
5. Cost – One server instead of three.
The formula: when a distributed monolith is enough
Team < 5 developers, load < 1000 RPS, deploys < 5/day, one server handles it, no requirements for independent scaling.
Step-by-step evolution plan
Phase 1: Hardening (effort: low, impact: 80%)
- Split Redis into separate instances per service
- Version the shared package with semver
- Add circuit breaker in RemoteServiceClient
Phase 2: Infrastructure independence
- Separate PostgreSQL instances
- Split docker-compose per service
- API contracts between services
Phase 3: True microservices (team > 5)
- Service discovery instead of env vars
- Message queue for async operations
- Independent CI/CD pipelines
Conclusion
A distributed monolith is not a diagnosis. It's a stage in architectural evolution. 80% of microservice benefits can be achieved with 20% of the effort – by splitting Redis, adding a circuit breaker, and versioning your shared package.
Sign up: legal.org.ua