Faysal Ahmed
Chapter 8

Integration, APIs, and Microservices

API design, event-driven integration, service decomposition, and microservice pitfalls.

API Design

ProtocolData FormatBest ForStreaming
RESTJSON, XMLCRUD, public APIs, simple request-responseNo
GraphQLJSON (query language)Complex UIs, multiple data sources, mobile appsSubscriptions
gRPCProtocol Buffers (binary)Internal service-to-service, high-perf, real-timeYes (native)
Figure 8.1 — API protocol comparison. Choose the right protocol for each interface boundary.

REST

Resource-oriented, stateless, uses standard HTTP methods.

GET    /orders          # List orders
POST   /orders          # Create order
GET    /orders/{id}     # Get order detail
PATCH  /orders/{id}     # Update order
DELETE /orders/{id}     # Delete order

GraphQL

Single endpoint, client specifies exactly what data it needs.

query {
  order(id: "123") {
    id, status, items { product { name } }
  }
}

gRPC

Binary protocol, service contracts defined in .proto files, supports streaming.

service OrderService {
  rpc GetOrder (GetOrderRequest) returns (Order);
  rpc ListOrders (ListOrdersRequest) returns (stream Order);
}
API-first design

Define the API contract before implementation. This forces clarity about what the system does and enables parallel front-end and back-end development. Use OpenAPI (REST), GraphQL SDL, or protobuf as your contract language.

Event-Driven Integration

Instead of one service calling another directly, services emit events and react to events.

┌──────────────┐
│    Order     │
│   Service    │
└──────┬───────┘
       │ order.created
       ▼
┌──────────────┐
│    Kafka     │
│   (Broker)   │
└──────┬───────┘
       │
       ├────────────────┬──────────────┐
       ▼                ▼              ▼
┌──────────┐    ┌────────────┐  ┌────────────┐
│Inventory │    │Notification│  │ Analytics  │
│ Service  │    │  Service   │  │  Service   │
└──────────┘    └────────────┘  └────────────┘
Figure 8.2 — Event-driven integration: the order.created event fans out to multiple independent consumers.

Pros: Loose coupling, many consumers, async.

Cons: Eventual consistency, debugging harder, schema evolution.

Schema management

Use a schema registry (e.g., Confluent Schema Registry) to manage event schema evolution. Without it, producers and consumers silently drift apart, causing runtime failures that are hard to diagnose.

Microservices

When to Decompose

IndicatorDescription
Team frictionTeams can’t move without stepping on each other
Different scaling needsSome features need more resources than others
Clear bounded contextsWell-defined domain boundaries exist

When NOT To

  • Team is small (< 10 people)
  • Domain is simple CRUD
  • You haven’t identified clear service boundaries yet
Start with a modular monolith

Extract services only when the pain of the monolith exceeds the pain of distribution. A well-structured monolith with clear module boundaries is easier to maintain, deploy, and reason about than a distributed mess.

Common Pitfalls

PitfallSymptomPrevention
Distributed monolithTight coupling across servicesClear bounded contexts, async where possible
Network chaosNo circuit breakers, retriesResilience patterns (timeouts, bulkheads)
Data inconsistencyDistributed transactions everywhereEmbrace eventual consistency, saga pattern
Over-splittingAny feature touches 5+ servicesStart coarse, split based on measured need
        ┌──────────┐    ┌──────────┐    ┌──────────┐
        │  Auth    │    │  Order   │    │ Payment  │
        │  Service │◄──►│  Service │◄──►│ Service  │
        └──────────┘    └──────────┘    └──────────┘
              │
        ┌─────┴─────┐
        │  User     │
        │  Service  │
        └───────────┘
Figure 8.3 — Microservice interaction. Each service owns its data and communicates via APIs or events.

Next: Chapter 9 — DevOps and Architecture Documentation