Intermediate → Advanced18 min read· Topic 8.2

Microservices design principles

Single responsibility, bounded context, API-first, database per service, twelve-factor app

🧩Key Takeaways

  • 1
    Single Responsibility: each service owns one business capability completely
  • 2
    Database per service: no shared databases — communication only via APIs or events
  • 3
    API-first design: define the contract before implementation, version APIs from day one
  • 4
    Twelve-Factor App: methodology for building cloud-native services (config in env, stateless processes, port binding)

Principles That Actually Matter

Microservices succeed or fail based on how well you define service boundaries. The most common failure mode: splitting services too small (nano-services) or along technical layers instead of business capabilities.

A well-designed microservice is independently deployable, owns its data, and maps to a bounded context from domain-driven design.

Core Principles

Each service should own one complete business capability: Order Service handles everything about orders (CRUD, status, history).

Use DDD bounded contexts: the 'User' in the Auth service is different from 'User' in the Billing service. Each has its own model.

Red flag: if deploying Service A always requires deploying Service B, they should probably be one service.

Each service owns its database — no other service can read or write directly. Data sharing happens through APIs or events.

This is the hardest principle to follow. Cross-service queries feel natural but create tight coupling.

Pattern for cross-service data: API composition (query both services), CQRS (materialized views), or event-driven data replication.

Define the API contract (OpenAPI/protobuf) before writing code. Consumers and producers can develop in parallel.

Version APIs from day one: /v1/orders. Breaking changes = new version. Never break existing consumers.

Use semantic versioning for libraries, URL versioning for REST, package versioning for gRPC.

1. Codebase: One repo per service. 2. Dependencies: Explicitly declare. 3. Config: Environment variables (never hardcode). 4. Backing services: Treat databases as attached resources.

5. Build/release/run: Strict separation. 6. Processes: Stateless, share-nothing. 7. Port binding: Self-contained HTTP server. 8. Concurrency: Scale out via processes.

9. Disposability: Fast startup, graceful shutdown. 10. Dev/prod parity: Keep environments similar. 11. Logs: Write to stdout. 12. Admin processes: Run as one-off tasks.

Advantages

  • Clear boundaries enable team autonomy
  • Independent deployment reduces risk
  • API-first enables parallel development

Disadvantages

  • Database per service complicates cross-service queries
  • Distributed transactions are much harder
  • Requires strong organizational discipline

🧪 Test Your Understanding

Knowledge Check1/1

What's the 'database per service' principle?