Menu

Topic 1 of 8

Service Boundaries And Contracts

Learn Service Boundaries And Contracts for free with explanations, exercises, and a quick test (for Backend Engineer).

Published: January 20, 2026 | Updated: January 20, 2026

Why this matters

Defining clear service boundaries and reliable contracts prevents distributed monoliths, reduces coordination overhead, and makes changes safer. In a Backend Engineer role, you will:

  • Split a large domain into cohesive services with owned data.
  • Design HTTP APIs and event contracts that are resilient and versioned.
  • Handle compatibility, retries, idempotency, and failure modes.
  • Collaborate with other teams using consumer-first contracts.

Concept explained simply

Service boundary = what a service owns and is responsible for: its domain rules, data, and SLAs. Think of it as the fence around a piece of the business.

Contract = how others interact with the service: request/response APIs and asynchronous event schemas. Contracts are promises you must keep compatible as your service evolves.

Mental model

  • Picture services as LEGO bricks. Each brick has a clear shape (responsibilities) and ports (contracts). Bricks connect cleanly or not at all.
  • If two parts of code always change together, they probably belong in the same brick.
  • If a feature requires cross-service transactions, prefer eventual consistency via events or sagas instead of stretching boundaries.

How to find good boundaries

Heuristics you can apply
  • Cohesion of behavior: Group rules that belong to the same business capability (e.g., Payments authorizes and captures money; Orders tracks order lifecycle).
  • Change together: Components that change for the same reason should live together. If a change regularly requires coordinated deploys across services, your split is off.
  • Data ownership: One service is the source of truth for a dataset. Others read via APIs or replicas; they do not write directly.
  • Transaction boundaries: If you need atomicity across functions, consider them one service; otherwise use eventual consistency patterns.
  • External dependencies: Integrations with distinct SLAs/rate limits (e.g., payment gateways, email providers) often justify a separate boundary.
  • Team alignment: Align services to team ownership. Cross-team changes should be API-level, not DB-level.
  • Regulatory isolation: PII or sensitive domains can be isolated for compliance and auditability.

Contract design essentials

  • API shape: Use clear resources and verbs. Be explicit about required/optional fields.
  • Validation: Fail fast with useful error details. 400/422 for validation; 404 for missing; 409 for conflicts.
  • Idempotency: Support safe retries for create/update by using an idempotency key and deterministic server behavior.
  • Pagination and filtering: Consistent query parameters and stable sort keys.
  • Time/outages: Set timeouts. Document retry policies and backoff expectations.
  • Rate limits: Communicate limits and remaining quota in headers or fields.
  • Security: Authentication, authorization, encryption in transit. Avoid leaking internal identifiers unnecessarily.
  • Error model: Structured errors with machine-readable codes and human-friendly messages.
  • Versioning: Prefer backward-compatible, additive changes. For breaking changes, introduce a new version and deprecate with a sunset period.
  • Events: Name event types clearly, include a schema version, timestamps, ids, and correlation ids. Ensure at-least-once delivery is safe via idempotency.
Quick checklist for your contract
  • Resource names and fields are descriptive and stable.
  • All non-200 responses have a structured body with code and message.
  • Idempotency key supported for non-safe operations.
  • Pagination is consistent and documented.
  • Backward compatibility plan exists (additive by default, versioned when breaking).
  • Events include schemaVersion, eventId, occurredAt, correlationId, and an aggregateId.
  • Security and PII handling are explicit.

Worked examples

E-commerce: Orders and Payments

Boundaries:

  • Orders Service owns order lifecycle, status, and order lines.
  • Payments Service owns payment intents, authorization, capture, refund, and gateway integration.

Contracts:

Orders HTTP endpoints (sample)
{
  "POST /orders": {
    "request": {"customerId": "uuid", "items": [{"sku": "string", "qty": 2}]},
    "idempotency": "Idempotency-Key header",
    "responses": {
      "201": {"orderId": "uuid", "status": "PENDING"},
      "422": {"code": "INVALID_ITEM", "message": "SKU not found"}
    }
  },
  "GET /orders/{orderId}": {"200": {"orderId": "uuid", "status": "CONFIRMED"}}
}
Payments events (sample)
{
  "type": "payment.authorized",
  "schemaVersion": 1,
  "eventId": "uuid",
  "occurredAt": "2025-09-13T12:00:00Z",
  "correlationId": "uuid-of-order",
  "data": {"paymentIntentId": "uuid", "amount": 2599, "currency": "USD"}
}

Flow: Orders creates an order, emits order.created. Payments listens, creates a payment intent, and on authorization emits payment.authorized. Orders updates status upon receiving the payment event. No shared database.

Users and Notifications

Boundaries:

  • User Service: profile, preferences, identity linkage.
  • Notification Service: email/SMS delivery, templates, provider retries.
Contract sketch
// User emits
{
  "type": "user.registered",
  "schemaVersion": 1,
  "eventId": "uuid",
  "data": {"userId": "uuid", "email": "alice@example.com"}
}

// Notification API for delivery status
GET /notifications/{id}
200 {"id":"uuid","status":"DELIVERED"}
404 {"code":"NOT_FOUND","message":"..."}

Notification retries delivery with backoff and idempotency keys per provider request to avoid duplicates.

Travel booking: Inventory and Pricing

Boundaries:

  • Inventory: seat/room availability; reservations and release windows.
  • Pricing: fare rules and dynamic price calculations.
Contract choices
  • Sync API for price quote at search time: GET /prices?route=...&date=...
  • Async events for price rule updates: pricing.rule.updated v2 (additive fields only).
  • Inventory never calls Pricing DB; it requests prices via API and caches with TTL.

Who this is for

  • Backend Engineers moving from monoliths to microservices.
  • Developers integrating with external providers or cross-team APIs.
  • Tech Leads defining service ownership and collaboration boundaries.

Prerequisites

  • Comfortable with HTTP, JSON, and basic REST patterns.
  • Basic message queue/event knowledge (publish/subscribe, at-least-once).
  • Familiarity with data modeling and transactions.

Learning path

  1. Map the domain into capabilities and data ownership.
  2. Draft API and event contracts with idempotency and error models.
  3. Add resiliency: timeouts, retries, rate limits, and circuit breakers.
  4. Plan compatibility: additive changes first; version when breaking.
  5. Validate via consumer-driven contract tests before coding integrations.

Exercises

Do these in order. A quick checklist is included to track yourself.

  1. Exercise ex1: Propose service boundaries for a food delivery app (merchants, menus, orders, payments, delivery).
  2. Exercise ex2: Design a create-and-fetch API contract for Orders with idempotency and clear errors.
  3. Exercise ex3: Evolve an event schema without breaking consumers; plan v1 to v2 rollout.
Checklist: ready to move on?
  • I identified data ownership and avoided shared databases.
  • My API has idempotency for non-safe operations.
  • I defined error codes and structures.
  • I documented pagination and filtering.
  • I planned a backward-compatible path and deprecation window.
  • My events include schemaVersion, eventId, occurredAt, and correlationId.

Common mistakes and self-checks

  • Shared DB between services: Smells like tight coupling. Self-check: Can you deploy one service independently without schema coordination?
  • Chatty APIs: N+1 cross-service calls. Self-check: Can you batch or pre-compute? Would a read model or cache help?
  • Hidden breaking changes: Renaming fields or changing types. Self-check: Would an old client still parse and behave correctly?
  • No idempotency: Duplicate charges or orders. Self-check: If the client retries, will the outcome be exactly once?
  • Leaky abstractions: Exposing internal DB ids or internal error messages. Self-check: Is the contract expressing business concepts?

Practical projects

  • Split a simple monolith shop into two services: Catalog and Orders. Add an API gateway or simple reverse proxy and write contracts.
  • Emit order.created and payment.authorized events and build a small dashboard service that projects a read model.
  • Introduce a backward-compatible change (add a field) and demonstrate old client compatibility plus a deprecation notice.

Next steps

  • Complete the exercises below, then take the Quick Test.
  • Iterate your contracts with feedback from a mock "consumer" (another teammate or your future self).
  • Note: The Quick Test is available to everyone. Log in to save your progress.

Mini challenge

Design a minimal contract for a Refunds service that receives a refund request and emits refund.completed. Include idempotency, error model, and an event schema with correlation id. Keep it additive-friendly.

Ready for the Quick Test?

When your exercises feel solid and your checklist is all checked, jump into the test to validate your understanding.

Practice Exercises

3 exercises to complete

Instructions

Split the domain into services: Merchants, Menus, Orders, Payments, Delivery, Customers. For each service list:

  • Responsibilities (verbs)
  • Owned data (source of truth)
  • Inbound contracts (APIs/events it exposes)
  • Outbound dependencies (APIs/events it consumes)
  • Why this split reduces cross-service transactions

Avoid shared tables. Prefer eventual consistency where needed.

Expected Output
A clear list of 4–6 services with responsibilities, owned data, and a short justification per boundary.

Service Boundaries And Contracts — Quick Test

Test your knowledge with 10 questions. Pass with 70% or higher.

10 questions70% to pass

Have questions about Service Boundaries And Contracts?

AI Assistant

Ask questions about this tool