Architecture & Technical Design · master area
Service Composition
Two services need to talk. The interesting question is not "REST or GraphQL" — it is "request-response or event-driven." The composition choice is an ADR-worthy decision; the failure modes of each are different.
Owners: Tech Lead Phase it lives in: What We Build → How We Build The corpus principle this enacts: Translation lives at the boundary.
Where it lives in the chain
- How We Build · Service composition and DX — the canon
How to do this
The ADR names the composition pattern for each integration:
| Pattern | When | Failure mode |
|---|---|---|
| Request-response (sync) | Caller blocks on the answer. Gal submits the exam, waits for the grade. | Provider down → caller's call fails. Cascading failures. Need timeouts, retries, fallback. |
| Event-driven (async) | Caller publishes a fact, consumers react. ExamGraded event → Billing creates a fee asynchronously. | Event lost → eventual consistency window grows. Need at-least-once delivery, idempotent consumers, dead-letter queues. |
| Workflow (orchestration) | Long-running, multi-step. Grading → review → publish → notify. Compensations on failure. | State machine in one place. Visible. Recoverable. Heavier infrastructure. |
| Choreography (no orchestrator) | Services react to each other without central coordination. | Lighter. Harder to reason about end-to-end. No single place to ask "what happened to that exam?" |
What good practice looks like
The choice matches the business semantics:
- "Did the grade reach the teacher?" — request-response. The teacher waits for confirmation.
- "Bill the school for the grade." — event-driven. The teacher does not wait for billing.
- "Publish the result to three downstream systems." — event-driven, with retry on each consumer.
- "Refund a payment, then notify, then close the ticket." — workflow. The compensations matter.
A team that defaults to sync everywhere builds a distributed monolith — every service waits on every other service, and one slow service drags the whole system. A team that defaults to async everywhere builds a system nobody can debug — every action becomes a chain of events whose end-state surfaces eventually, somewhere, possibly. The discipline is choosing per integration, with the ADR open.
Related crafts
- ADR — where the composition choice is recorded
- Sequence Diagrams — where the choice becomes visible
- Bounded Context Mapping — what the services are composing across