Skip to content

Microservices Communication Project — Build a Complete Distributed System

DodaTech Updated 2026-06-28 7 min read

In this tutorial, you will learn about Microservices Communication Project. We cover key concepts, practical examples, and best practices to help you master this topic.

This capstone project combines all microservices communication patterns into a production-ready distributed system: REST and gRPC services, async message queues, event bus, service discovery, circuit breakers, distributed tracing, and saga coordination.

What You'll Learn

By the end of this project you will design and implement a complete microservices communication architecture, integrate multiple communication patterns in a single system, handle real-world failure scenarios, and deploy services with proper observability.

Why It Matters

Real microservices systems rarely use a single communication pattern. They combine sync and async, REST and gRPC, queues and event buses. This project teaches you to design integrated communication architectures that handle the complexity of production systems.

Real-World Use

This project mirrors DodaZIP's production architecture: an API gateway with circuit breakers, gRPC for internal real-time communication, RabbitMQ for job queues, an event bus for domain events, service discovery via Consul, and OpenTelemetry tracing across all services.

flowchart TB
    subgraph "Project Architecture"
        A[API Gateway] -->|REST| B[User Service]
        A -->|REST| C[Order Service]
        B -->|gRPC| D[Payment Service]
        C -->|Async| E[(RabbitMQ)]
        E --> F[Worker Pool]
        C -->|Event Bus| G[Notification Service]
        C -->|Event Bus| H[Analytics Service]
        I[Consul] -.->|Service Discovery| A
        J[Jaeger] -.->|Tracing| A
        J -.->|Tracing| B
        J -.->|Tracing| C
    end

Project Overview

The system you will build.

# project_overview.py
# Project architecture overview

def project_overview():
    print("Microservices Communication Project Overview")
    print("=" * 45)
    print()
    print("Services:")
    print("  1. API Gateway (REST) - Single entry point with circuit breakers")
    print("  2. User Service (REST + gRPC) - User management")
    print("  3. Order Service (REST + Events) - Order processing")
    print("  4. Payment Service (gRPC) - Payment processing")
    print("  5. Worker Service (RabbitMQ) - Background jobs")
    print("  6. Notification Service (Event Bus) - Email/SMS")
    print("  7. Analytics Service (Event Bus) - Usage tracking")
    print()
    print("Infrastructure:")
    print("  - Consul: Service discovery + health checks")
    print("  - RabbitMQ: Job queues + event bus")
    print("  - Jaeger: Distributed tracing")
    print("  - PostgreSQL: Service databases")
    print()
    print("Communication Patterns Used:")
    print("  - REST: Gateway -> User, Gateway -> Order")
    print("  - gRPC: User -> Payment (unary)")
    print("  - RabbitMQ: Order -> Worker (work queue)")
    print("  - Event Bus: Order -> Notification, Analytics")
    print("  - Service Discovery: All services register in Consul")

project_overview()

Service Implementation

Detailed service implementations.

# service_implementations.py
# Service implementations

def service_implementations():
    print("Service Implementation Details")
    print("=" * 45)
    print()
    
    gateway_code = """
# API Gateway Service
# Communication: REST inbound, REST/gRPC outbound
# Patterns: Circuit breaker, service discovery, rate limiting

class OrderGateway:
    def __init__(self, consul_client, tracer):
        self.discovery = ServiceDiscoveryClient(consul_client)
        self.circuit_breaker = CircuitBreaker(failure_threshold=5)
        self.tracer = tracer
    
    def get_order_details(self, request):
        with self.tracer.start_span("gateway.get_order"):
            # Discover order service via Consul
            order_url = self.discovery.discover("order-service")
            
            # Call with circuit breaker
            def call_order():
                return httpx.get(f"{order_url}/orders/{request.order_id}")
            
            order_response = self.circuit_breaker.call(
                call_order, 
                fallback=lambda: {"status": "degraded", "data": None}
            )
            
            return order_response
"""
    print("API Gateway:")
    print(gateway_code)
    
    order_code = """
# Order Service
# Communication: REST inbound, RabbitMQ outbound, Event Bus outbound
# Patterns: CQRS, event-driven, saga participant

class OrderService:
    def __init__(self, rabbitmq, event_bus, db):
        self.rabbitmq = rabbitmq
        self.event_bus = event_bus
        self.db = db
    
    def create_order(self, order_data, trace_context):
        # 1. Save order to database
        order = self.db.save_order(order_data)
        
        # 2. Publish job to RabbitMQ for payment processing
        self.rabbitmq.publish("payment.jobs", {
            "order_id": order.id,
            "amount": order.total,
            "trace_id": trace_context.trace_id
        })
        
        # 3. Publish event to event bus for notification
        self.event_bus.publish(Event(
            topic="order.events",
            type="order.created",
            data={"order_id": order.id, "customer_id": order.customer_id},
            trace_id=trace_context.trace_id
        ))
        
        return order
"""
    print("Order Service:")
    print(order_code)

service_implementations()

Infrastructure Setup

Docker Compose configuration for the project.

# infrastructure.py
# Infrastructure setup

def infrastructure():
    print("Infrastructure Setup (Docker Compose)")
    print("=" * 45)
    print()
    
    docker_compose = """
version: '3.8'

services:
  consul:
    image: consul:1.15
    ports:
      - "8500:8500"
    command: agent -dev -client=0.0.0.0
  
  rabbitmq:
    image: rabbitmq:3-management
    ports:
      - "5672:5672"
      - "15672:15672"
  
  jaeger:
    image: jaegertracing/all-in-one:1.50
    ports:
      - "16686:16686"  # UI
      - "4317:4317"    # OTLP gRPC
  
  postgres:
    image: postgres:15
    environment:
      POSTGRES_USER: micro
      POSTGRES_PASSWORD: micro_pass
      POSTGRES_DB: microservices
  
  user-service:
    build: ./services/user
    depends_on: [consul, postgres, jaeger]
    environment:
      CONSUL_HOST: consul
      JAEGER_HOST: jaeger
      DATABASE_URL: postgresql://micro:micro_pass@postgres/microservices
  
  order-service:
    build: ./services/order
    depends_on: [consul, rabbitmq, postgres, jaeger]
    environment:
      CONSUL_HOST: consul
      RABBITMQ_HOST: rabbitmq
      JAEGER_HOST: jaeger
  
  payment-service:
    build: ./services/payment
    depends_on: [consul, postgres, jaeger]
  
  worker-service:
    build: ./services/worker
    depends_on: [rabbitmq, jaeger]
    deploy:
      replicas: 3  # Scale workers independently
  
  notification-service:
    build: ./services/notification
    depends_on: [rabbitmq, jaeger]
  
  api-gateway:
    build: ./services/gateway
    ports:
      - "8080:8080"
    depends_on: [consul, jaeger]
"""
    print(docker_compose)

infrastructure()

Deployment and Testing

Running and verifying the system.

# deployment_testing.py
# Deployment and testing

def deployment_testing():
    print("Deployment and Testing")
    print("=" * 45)
    print()
    
    test_code = """
# 1. Start infrastructure
docker-compose up -d consul rabbitmq jaeger postgres

# 2. Build and start services
docker-compose up -d --build

# 3. Verify service discovery (Consul UI)
curl http://localhost:8500/v1/catalog/services
# Expected: ["api-gateway", "user-service", "order-service", 
#            "payment-service", "worker-service", "notification-service"]

# 4. Test REST endpoint through gateway
curl -X POST http://localhost:8080/api/orders \\
  -H "Content-Type: application/json" \\
  -d '{"user_id": "u1", "items": [{"product": "p1", "qty": 2}], "total": 49.99}'
# Expected: {"order_id": "ord-1", "status": "created", "trace_id": "..."}

# 5. Verify RabbitMQ queues
curl http://localhost:15672/api/queues
# Expected: payment.jobs queue with messages

# 6. Verify tracing in Jaeger
# Open http://localhost:16686 and search for trace "POST /api/orders"
# Expected: spans across gateway, order, payment, worker

# 7. Test circuit breaker
# Stop payment-service and make 5+ requests
# Expected: 5th request returns degraded response, 
#           subsequent requests fail fast

# 8. Test service discovery recovery
docker-compose stop payment-service
# Wait 30s for health check failure
# Expected: payment-service removed from Consul
docker-compose start payment-service
# Wait 10s for health check success
# Expected: payment-service re-registered in Consul
"""
    print("Testing Commands:")
    print(test_code)

deployment_testing()

Common Mistakes

  1. Building all services at once: Start with one service and its communication channel. Verify it works end-to-end before adding more services.

  2. Ignoring service discovery in development: Using hardcoded IPs in development creates surprises in production. Use service discovery everywhere, including local Docker environments.

  3. Not testing failure scenarios: A system that only works when everything is healthy will fail in production. Test circuit breaker openings, service crashes, and broker outages.

  4. Missing observability from the start: Add distributed tracing, metrics, and structured logging to every service from day one. Debugging a microservices system without observability is impossible.

  5. Skipping Contract Testing: Changes to one service's API break other services. Implement contract tests (Pact, Spring Cloud Contract) to catch breaking changes during CI.

Practice Questions

  1. What is the most important infrastructure component for service discovery? Consul (or similar registry). All services register here, and the gateway queries it to find service instances.

  2. Why use both RabbitMQ and an event bus in the same project? RabbitMQ handles work queues (one job to one worker). The event bus handles pub/sub (one event to many consumers). They serve different purposes.

  3. What is the role of the API gateway in this project? Single entry point for external requests, circuit breaker protection, service discovery integration, and request routing.

  4. How does Jaeger help operate this system? Jaeger traces requests across all services, showing where time is spent and which services fail, making performance debugging possible.

  5. Challenge: Extend this project with a choreography saga for the order lifecycle. Define events for OrderCreated, PaymentProcessed, InventoryReserved, OrderShipped. Implement compensating events for PaymentFailed and OutOfStock scenarios.

FAQ

What is the goal of this project?

To combine all microservices communication patterns (REST, gRPC, message queues, event bus, service discovery, circuit breakers, tracing, sagas) into a single working system.

How many services should I build?

Start with 4-5 core services: gateway, user, order, payment, worker. Add notification and analytics once the core works.

Do I need to deploy this to the cloud?

No. Run everything locally with Docker Compose. The same architecture works on any orchestrator.

How long does this project take?

Expect 2-4 days for a complete implementation: 1 day for infrastructure, 1-2 days for services, 1 day for testing and debugging.

What is the most important lesson from this project?

That microservices communication is hard and each pattern has tradeoffs. No single pattern works for everything. Hybrid architectures are the norm.

Mini Project

This entire lesson is the mini project. Build the complete system described above, deploy it with Docker Compose, and verify all communication patterns work. Create a test suite that exercises each pattern: REST calls through the gateway, gRPC calls for payment, RabbitMQ jobs for workers, event bus for notifications, circuit breaker failure/recovery, and distributed tracing visualization in Jaeger.

def project_completion():
    print("Project Completion Checklist")
    print("=" * 45)
    print()
    print("[ ] All 7 services implemented")
    print("[ ] Docker Compose deploys all services")
    print("[ ] Consul shows all registered services")
    print("[ ] REST endpoint works through gateway")
    print("[ ] gRPC payment call succeeds")
    print("[ ] RabbitMQ worker processes jobs")
    print("[ ] Event bus delivers to notification + analytics")
    print("[ ] Circuit breaker opens on payment failure")
    print("[ ] Circuit breaker recovers when payment restarts")
    print("[ ] Jaeger shows complete traces across services")
    print("[ ] Service deregisters on crash in Consul")
    print("[ ] Service re-registers on recovery in Consul")
    print("[ ] Health endpoints respond on all services")
    print()
    print("Bonus:")
    print("[ ] Saga pattern for order lifecycle")
    print("[ ] Contract tests between services")
    print("[ ] Rate limiting on API gateway")
    print("[ ] Structured logging with trace IDs")

project_completion()

What's Next

Next: Serverless Tutorials for Serverless architecture patterns.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro