Integration Contract Mental Model
Goal
The previous chapters chose compute and data homes. This chapter asks how systems talk to each other once those homes exist.
Integration is not only "connect service A to service B." It is the promise one system makes to another: I will answer now, I will accept work for later, I will notify whoever cares, or I will coordinate a longer process until it reaches a known state.
The Short Version
The communication pattern should match the promise being made.
Use this first-pass map:
Need immediate answer -> API
Need durable work buffer -> queue
Need notify many consumers -> topic or event bus
Need coordinate steps -> workflow
Need do the work -> Lambda or container worker
AWS service names come after the contract. API Gateway, SQS, SNS, EventBridge, Step Functions, Lambda, and container workers are easier to compare once you know whether the design needs a call, a buffer, a notification, a route, an orchestration, or a processor.
What The Contract Promises
Every integration pattern carries a different promise:
| Pattern | Promise | Hidden Question |
|---|---|---|
| Synchronous API | "I will give you an answer during this request." | What happens if the dependency is slow or unavailable? |
| Queue | "I will durably accept work and process it when a consumer is ready." | How are retries, duplicates, and poison messages handled? |
| Pub/sub notification | "I will announce something so subscribers can react." | Who owns the event schema and subscriber impact? |
| Event routing | "I will route events from producers to interested targets." | How are rules, permissions, replay, and governance managed? |
| Workflow | "I will track a multi-step process with state, branching, and retries." | Which steps are coordinated, compensated, or timed out? |
This is why synchronous communication is not always the safest default. A direct call is simple when the caller truly needs an immediate answer. It is risky when the caller is only waiting for downstream side effects that could happen later.
Example: Checkout And Side Effects
An order API may need to validate the request, create an order record, and return a clear customer-facing state. That part is request/response.
But the same order may also trigger fulfillment, email, analytics, loyalty points, partner notification, and warehouse updates. If every side effect must complete before the customer sees confirmation, the customer experience becomes dependent on every downstream system.
A better design separates the promises:
Customer checkout
-> API: accept order and return state
-> Queue: process fulfillment work
-> Event: notify interested systems
-> Workflow: coordinate payment, inventory, and exceptions when needed
The design is not "async everywhere." It is "make each promise explicit."
Contract Details That Matter
The message or API payload is only part of the contract. A useful integration contract also includes:
Ownership: who owns the producer, consumer, and schema.
Security: who can call, publish, consume, or replay.
Ordering: whether order matters and where it matters.
Delivery: whether duplicate delivery is possible.
Retry: what should happen after failure.
Timeout: when a step should stop waiting.
Observability: how the flow is traced across services.
Compatibility: how schema changes affect older consumers.
Recovery: how failed messages, events, or workflow steps are investigated.
These questions belong in architecture design, not only implementation tickets.
Common Mistakes
Using synchronous calls for every downstream side effect.
Emitting events without clear ownership or schema expectations.
Treating asynchronous design as automatically reliable.
Ignoring how to trace a request after it becomes messages and events.
Promising global ordering or exactly-once outcomes without designing the required controls.
If Someone Asks You
How do you decide between API, queue, event, and workflow patterns?
A good answer is:
I start with the communication promise. If the caller needs an immediate answer, I consider an API. If work should be accepted and processed later, I use a queue. If many systems need to react, I use pub/sub or event routing. If a long-running process needs state, retries, branching, and compensation, I use a workflow. Then I define ownership, schema, retry behavior, idempotency, security, observability, and recovery.
What You Should Remember
Integration starts with the contract, not the middleware.
Questions to Check Your Understanding
What promise does a queue make differently from an API call?
Why can events reduce coupling but increase governance needs?
When does workflow orchestration become useful?