DEV Community

Cover image for Microservices
Gouranga Das Samrat
Gouranga Das Samrat

Posted on

Microservices

One-liner: Instead of one giant application (monolith), microservices split the system into small, independent services — each owning its own data and deployed separately.


📌 Monolith vs Microservices

Monolith

[One Big App]
 ├── User Module
 ├── Order Module
 ├── Payment Module
 ├── Notification Module
 └── [One Database]
Enter fullscreen mode Exit fullscreen mode

Deploy anything → redeploy everything. One bug → whole app down.

Microservices

[User Service]         → [Users DB]
[Order Service]        → [Orders DB]
[Payment Service]      → [Payments DB]
[Notification Service] → [Notif DB]
Enter fullscreen mode Exit fullscreen mode

Each service: independent codebase, independent deployment, independent database.


✅ Pros of Microservices

Benefit Detail
Independent deployment Deploy Payment without touching User Service
Independent scaling Scale only the Order Service during sales
Tech diversity Order Service in Go, ML Service in Python
Fault isolation Notification Service down → Orders still work
Small teams Each team owns one service (Conway's Law)
Easier to understand Each service is small and focused

❌ Cons of Microservices

Problem Detail
Network overhead In-process calls → network calls (latency + failure)
Distributed tracing Debugging a request across 10 services is hard
Data consistency No cross-service ACID transactions
Operational complexity 20 services = 20 deployments, 20 logs, 20 monitors
Service discovery How does Service A find Service B?
Testing Integration testing is much harder

🔄 Inter-Service Communication

Synchronous (Request-Response)

Order Service → HTTP/gRPC → Payment Service → response
Enter fullscreen mode Exit fullscreen mode

✅ Simple, immediate response

❌ Tight temporal coupling — both must be up

❌ Cascading failures

When to use: When the caller needs the result immediately (e.g., payment confirmation before showing success page)

Asynchronous (Event-Driven)

Order Service → [Event Bus / Queue] → Payment Service
                                    → Email Service
Enter fullscreen mode Exit fullscreen mode

✅ Decoupled — services can be down

✅ Natural fan-out

❌ Eventual consistency

❌ Harder to debug

When to use: Notifications, analytics, side effects that don't block the user


🏗️ Service Communication Patterns

API Gateway Pattern

Client → [API Gateway] → routes to appropriate service
                       → handles auth, rate limiting for all
Enter fullscreen mode Exit fullscreen mode

Service Mesh (Advanced)

[Service A] ←──[sidecar proxy]──[sidecar proxy]──► [Service B]
                  (Envoy)              (Envoy)
Enter fullscreen mode Exit fullscreen mode

Sidecar handles: mTLS, retries, timeouts, circuit breaking, observability.
Examples: Istio, Linkerd, AWS App Mesh.

BFF (Backend for Frontend)

[Mobile App] → [Mobile BFF] → aggregates calls to services
[Web App]    → [Web BFF]    → different data shape per client
Enter fullscreen mode Exit fullscreen mode

🗄️ Database Per Service

Each service owns its data. No shared DB.

❌ Wrong:
[User Service]  ──┐
[Order Service] ──┼──► [Shared DB]  ← tight coupling at data level
[Payment Service] ┘

✅ Right:
[User Service]    → [User DB (PostgreSQL)]
[Order Service]   → [Order DB (MongoDB)]
[Payment Service] → [Payment DB (PostgreSQL)]
Enter fullscreen mode Exit fullscreen mode

But what about joins?

  • Denormalize data (copy what you need)
  • Use API calls to get data from another service
  • Use event-driven data replication (each service maintains its own read model)

🔒 Saga Pattern — Distributed Transactions

No cross-service ACID transactions. Use Sagas instead.

Choreography Saga (Event-Driven)

Order Created →
  Payment Service listens → charges card → Payment Succeeded event
  Inventory Service listens → reserves stock → Stock Reserved event
  Email Service listens → sends confirmation

If payment fails → Payment Failed event →
  Order Service listens → cancels order
Enter fullscreen mode Exit fullscreen mode

Orchestration Saga (Central Coordinator)

[Saga Orchestrator]
  1. → tell Payment Service to charge
  2. → tell Inventory to reserve
  3. → tell Email to send confirmation

If step 2 fails:
  → tell Payment Service to refund (compensating transaction)
Enter fullscreen mode Exit fullscreen mode

🌡️ Circuit Breaker Pattern

Prevent cascading failures:

Normal:   Service A → Service B (works fine)
Degraded: Service A → Service B (Service B is slow/erroring)
Open:     Service A → [Circuit Open] → return fallback (don't call B)
          Wait 30 seconds → try again (half-open)
          If OK → close circuit → normal again
Enter fullscreen mode Exit fullscreen mode

States: Closed (normal) → Open (failing, reject calls) → Half-Open (testing recovery)

Libraries: Hystrix (Java), resilience4j, Polly (.NET), opossum (Node.js)


🔍 Observability in Microservices

Distributed Tracing

Request ID: abc-123
→ API Gateway     [10ms]
→ Order Service   [50ms]
→ Payment Service [200ms]  ← slow here!
→ Email Service   [30ms]
Total: 290ms
Enter fullscreen mode Exit fullscreen mode

Tools: Jaeger, Zipkin, AWS X-Ray, Datadog APM

Centralized Logging

All services ship logs to one place: ELK Stack (Elasticsearch + Logstash + Kibana), Datadog, CloudWatch.

Metrics & Alerting

Prometheus + Grafana: CPU, memory, request rate, error rate, latency (p50, p95, p99).


🎨 Diagram

The diagram shows:

  • Monolith vs microservices comparison
  • API Gateway + service mesh layout
  • Saga orchestration flow
  • Circuit breaker states

🔑 Key Takeaways

  • Don't start with microservices — start with a well-structured monolith and extract services when you hit scaling/team pain
  • Database per service is non-negotiable for true independence
  • Sagas replace distributed transactions (embrace eventual consistency)
  • Circuit breakers are essential — one slow service will take down the whole system without them
  • Observability (tracing, logging, metrics) is twice as important in microservices

Top comments (0)