
Monolith vs Microservices
Monolith vs Microservices for E-commerce Systems
When building an e-commerce backend (authentication, products, cart, orders, payments), the core challenge is:
- How should we structure the backend so it scales with users, teams, and system complexity?
- How do we ensure reliability during failures, especially in critical flows like payments?
Choosing architecture early impacts:
- development speed
- scalability
- operational complexity
- failure handling
Why This Matters in Real Systems
In real-world e-commerce platforms:
- Traffic is unpredictable (flash sales, spikes)
- Payments must be consistent and reliable
- Multiple teams own separate domains (auth, orders, payments)
- Failures are inevitable (network issues, service downtime)
A poor architecture can cause:
- slow deployments
- system-wide failures
- inability to scale bottleneck components
- team coordination bottlenecks
Monolithic Architecture
A monolith is a single deployable application where all modules run together.
High-Level Structure
/ecommerce
/auth
/products
/cart
/orders
/payments
index.ts
Request Flow
Client → API Server
↓
Auth + Cart + Orders + Payments (same process)
↓
Single Database
Core Characteristics
-
In-process communication
- Modules call each other via function calls
- Very fast (no network overhead)
-
Shared database
- All modules use one DB
- Supports joins and strong ACID transactions
-
Simple deployment
- One codebase, one pipeline, one deployment unit
-
Easier debugging
- Centralized logs and stack traces in one runtime
Where Monoliths Break at Scale
-
Deployment bottleneck
- Any small change requires full redeploy
-
Inefficient scaling
- Cannot scale only one domain (e.g., payments)
- Entire app scales, wasting resources
-
Tight coupling
- Changes in one module can unexpectedly affect others
-
Large blast radius
- One bug can impact or crash the full system
-
Team coordination overhead
- Merge conflicts and release coordination increase
When Monolith Is the Right Choice
- Small team (roughly ≤ 5–10 developers)
- MVP or early-stage product
- Speed of iteration is more important than advanced scalability
Microservices Architecture
Microservices split the backend into independent services, each owning a business domain.
High-Level Architecture
Client → API Gateway
↓
Auth Service
Product Service
Cart Service
Order Service
Payment Service
↓
Each service has its own database
How Microservices Communicate
- Synchronous: REST / gRPC (real-time calls)
- Asynchronous: Kafka / RabbitMQ (event-driven workflows)
Core Characteristics
-
Network-based communication
- Services talk over HTTP/gRPC/messages
- Adds latency and failure modes
-
Database per service
- Each service owns its data
- Reduces cross-domain coupling
-
Independent deployment
- Teams release services separately
- Faster domain-specific release cycles
Real-World Critical Flow: Order + Payment
- User places order
- Order Service creates order
- Payment Service processes payment
- If payment fails, order must be compensated/cancelled
This introduces distributed consistency challenges.
Common Problems in Microservices
-
Network failures
- Timeouts, retries, partial failures are normal
- Function-call assumptions no longer hold
-
Data consistency complexity
- No single shared DB
- No global ACID transaction across services
- Usually relies on eventual consistency
-
Distributed failure scenarios
- Example: order created, payment fails
- Without compensation, system becomes inconsistent
-
Harder debugging
- Logs are distributed across services
- Requires centralized logging + tracing
Key Design Patterns for Reliability and Scale
1) Strangler Pattern (Migration)
Instead of full rewrite:
- keep monolith running
- extract domains gradually
- route traffic incrementally to new services
This reduces migration risk.
2) Canary Deployment (Safer Releases)
Roll out gradually:
- 10% → 30% → 50% → 100%
Detect issues early before full impact.
3) Saga Pattern (Distributed Transaction Management)
Maintain cross-service consistency via compensating actions.
Example:
- Create order
- Process payment
- If payment fails → cancel order
Approaches:
- Orchestration: central coordinator controls steps
- Choreography: services react to events
4) Outbox Pattern (Reliable Event Publishing)
Problem:
- DB update succeeds, but event publish fails → inconsistency
Solution:
- write business data + event record in same DB transaction
- background worker publishes pending outbox events
Prevents lost events.
Trade-offs: Monolith vs Microservices
| Aspect | Monolith | Microservices |
|---|---|---|
| Complexity | Low | High |
| Performance | Fast (in-process) | Slower (network overhead) |
| Scalability | Limited | High (selective) |
| Deployment | Single unit | Independent services |
| Failure Impact | High blast radius | Better isolation |
| Data Consistency | Strong ACID (single DB) | Eventual consistency |
Common Mistakes
- Starting with microservices too early
- Ignoring retries/timeouts/idempotency
- Poor service boundary design
- Treating microservices as only “splitting code”
- Missing observability (logs, metrics, traces)
Final Takeaway
- Monoliths are simpler, faster to build, and ideal early.
- Microservices enable scale and team autonomy, but add major operational complexity.
A practical strategy:
- Start with a well-structured monolith
- Move to microservices only when clear scaling/team boundaries demand it