skip to content

Integration Patterns

Enterprise integration patterns: channels, message types for commands, events and queries, pipes and filters, correlation IDs, idempotent receivers, and process-manager orchestration. They are the shared vocabulary for describing how systems that were never designed together end up cooperating.

part ofSoftware design & architectureoverview, primer and where to startread it →
on this pageshow

questions

6

In enterprise integration, three common message types are commands, events, and queries. What distinguishes them, and how does each affect coupling between sender and receiver?

level: juniorimportance: must knowfreq 55%

answer

  1. command = imperative, one receiver
  2. event = past-tense fact, broadcast
  3. query = side-effect-free read
  4. coupling axis: cardinality + expectation
  5. EIP book: Command/Event/Document Message

basics

~20 s

A command tells one system to do something specific, like 'ship this order.' An event announces that something already happened, like 'order shipped,' and anyone can listen. A query just asks for data without changing anything. Each creates a different kind of dependency between sender and receiver.

solid answer

~40 s

Commands are imperative, addressed to a single known receiver, and the sender expects that receiver to perform an action and usually confirm success or failure — this creates tight coupling to that receiver's capability. Events are past-tense facts broadcast with no addressee in mind; any number of subscribers may react, and the publisher has no expectation about what they do — this decouples publisher from consumers. Queries request data synchronously without side effects, coupling the caller to a data contract and to the receiver's availability, but not to its behavior. Choosing the right type shapes how easily the system can evolve: commands centralize control, events distribute it, queries centralize read access.

go deeper

for a junior

Should correctly state the three types and give one distinguishing feature each (imperative/past-tense/read-only) without confusing them.

for a middle

Should connect the type to coupling (who depends on whom) and give a correct example of each in a request/response vs. broadcast context.

for a senior

Should discuss the trade-offs (control vs. flexibility), explain why commands need correlation/idempotency concerns that events don't, and recognize when overuse of one type creates a design smell.

for a principal

Should reason about how the mix of message types shapes the system's evolvability and failure blast radius, and give guidance for choosing between them at an architecture-review level, including when synchronous queries quietly reintroduce coupling.

## The three shapes **Enterprise Integration Patterns** (Hohpe & Woolf) formalizes messages sent between systems into shapes that carry different intent, and command, event, and query are the three shapes that show up in almost every integration design. | Message | What it is | |---|---| | **Command** | imperative and named for the action requested — `CreateOrder`, `CancelSubscription` — addressed to exactly one logical receiver that is expected to know how to perform it. | | **Event** | declarative and named in the past tense — `OrderCreated`, `PaymentFailed` — describing a fact that already happened, with no addressee: it is broadcast, and zero, one, or many consumers may pick it up. | | **Query** | asks a receiver to return information without producing any side effect, and unlike commands and events it is typically synchronous — the caller blocks (or awaits) until the answer arrives. | ## What actually separates them The mechanism that separates them is really about two independent axes: **cardinality** (one destination vs. any number of destinations) and **expectation** (does the sender expect a specific outcome to happen, or just an answer, or nothing at all). - A command's sender expects the receiver to change state and often wants to know whether it succeeded — so commands are frequently paired with a **reply channel** or a **correlation identifier** so the response can be matched back to the request. - An event's publisher makes no such demand; it fires the fact into a channel and moves on, which is what allows new subscribers to be added later without ever touching the publisher's code. - A query's sender wants data and typically nothing else, so queries have no state-changing effect and are safe to retry. ## How this maps onto coupling This distinction exists because it maps directly onto **coupling**, and coupling is the main cost you are managing in integration design. - A command couples the sender to the specific capability of one receiver: if `OrderService` sends a `ChargeCard` command straight to `PaymentService`, `OrderService` now depends on PaymentService's contract and availability, and if `PaymentService` is down the command cannot be delivered (or must be queued). - An event removes that dependency: `OrderService` publishes `OrderPlaced` and does not know or care whether `PaymentService`, `FraudService`, or nobody at all is listening — new consumers can be wired in without changing `OrderService`. - A query sits in between: it couples the caller to a data shape and to the callee being reachable right now, but not to any behavior being triggered. ## The trade-off The trade-off is **control versus flexibility**. - **Commands** give you certainty — you know (or can find out) whether the requested action happened, which matters when a business step is mandatory, like debiting an account. But that certainty costs tight coupling and often synchronous or near-synchronous waiting, and if the receiver's contract changes, every command sender must change too. - **Events** give you flexibility — consumers can be added, removed, or changed independently, and the publisher's blast radius stays small — but you give up the certainty: the publisher genuinely does not know if any listener acted, or acted correctly, or acted at all, which makes end-to-end behavior harder to reason about and debug. - **Queries** are cheap to add but each one is a synchronous dependency; too many chatty queries between services recreates tight coupling through the back door even in an otherwise event-driven design. ## How each shape fails Failure modes track these trade-offs. 1. With commands, the classic failure is an **ambiguous timeout**: the sender does not know if the receiver never got the command, got it and is still processing, or got it, executed it, and the acknowledgment was lost — this ambiguity is exactly why idempotent receivers and correlation identifiers matter for commands specifically. 2. With events, the common failure is an **implicit, undocumented workflow**: a business process ends up strung together across five services each reacting to the last one's event, with no single place that describes the overall flow, making failures hard to trace and recovery unclear — this is one of the pressures that leads teams toward an explicit saga or process manager. 3. With queries, the common failure is the **N+1 problem or synchronous fan-out**: a service that queries three others to answer one request inherits the combined availability and latency of all three, quietly reintroducing tight temporal coupling. ## Putting it together A concrete scenario: in an order-placement flow, `OrderService` sends a `PlaceOrder` command to itself (or a dedicated handler) which is answered with success/failure so the checkout UI can respond immediately. Once the order is persisted, `OrderService` publishes an `OrderPlaced` event; `InventoryService` and `ShippingService` each subscribe independently and reserve stock or schedule a shipment without `OrderService` knowing either of them exists. Later, a customer-support tool issues a `GetOrderStatus` query directly against `OrderService` to render a status page — a synchronous, side-effect-free read that would be the wrong shape for either a command or an event.

  • Why do commands typically need a correlation ID or reply channel, while events usually don't?
    Commands expect a specific outcome the sender cares about, so the sender needs a way to match an eventual response (or timeout) back to the original request — that's what a correlation ID does. Events are fire-and-forget broadcasts with no expected reply, so there's nothing to correlate back to the publisher; consumers process them independently and asynchronously.
  • If a service both sends commands and publishes events for the same business action, what design smell might that indicate?
    It often means the service is doing double duty as both the authority that decides an action should happen and the announcer that it happened, which can work but risks divergence if the command path and the event path fall out of sync. Cleaner designs usually publish the event only after the command's effect is durably committed, so the event is a true reflection of state rather than a parallel, possibly-inconsistent signal.
  • Why might overusing synchronous queries between services undermine an otherwise decoupled, event-driven architecture?
    Each query is a live runtime dependency: the caller's request now waits on the callee's latency and availability, and a chain of queries recreates a distributed monolith's coupling even if events are used elsewhere. It also reintroduces the fallacy of assuming the network is reliable — a slow or down query target degrades or breaks the caller in real time, unlike an event which can be replayed later.

A command is like calling a specific plumber and asking them to fix your sink; an event is like posting on a community board that your sink flooded, and whoever cares (neighbor, insurer, landlord) reacts on their own; a query is like calling the water company just to ask what your current usage reading is.

saying these in an interview costs you the question

  • calls an event 'CreateOrder' (imperative name) instead of 'OrderCreated'
  • expects a guaranteed synchronous reply from an event publish
  • cannot explain why queries have no side effects
  • treats commands and events as interchangeable 'just messages'
  • doesn't mention that command senders usually need to know success/failure

context

open as a page

What is the difference between a point-to-point messaging channel and a publish-subscribe channel, and how does that choice affect how many consumers can safely process the same message?

level: middleimportance: must knowfreq 60%

basics

~20 s

A point-to-point channel delivers each message to exactly one consumer, even if several are listening — good for work that should happen once. A publish-subscribe channel delivers a copy of each message to every subscriber, good for broadcasting facts to many independent listeners.

open as a page

In an asynchronous request/response flow over messaging, what problem does a correlation ID solve, and where does it need to be carried?

level: middleimportance: must knowfreq 65%

basics

~20 s

When messages travel through queues instead of direct calls, a reply can arrive long after (and out of order from) the request, so you need a shared ID stamped on both to know which reply answers which request.

open as a page

Why does an idempotent receiver need to detect duplicate messages explicitly, rather than relying on the messaging channel to guarantee exactly-once delivery?

level: seniorimportance: must knowfreq 70%

basics

~20 s

Most real messaging systems can only promise 'at least once' delivery, so the same message can arrive twice (e.g., after a retry). The receiver has to notice a duplicate itself and skip re-doing the work, or side effects like double-charging happen.

open as a page

In the pipes-and-filters integration pattern, what makes a processing step a good 'filter,' and what do you give up by chaining many small filters instead of one larger processing step?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A good filter does one transformation, takes input from a pipe and puts output on the next pipe, and doesn't know or care what's upstream or downstream. Chaining many small ones is flexible and reusable, but adds latency and more places for failures to hide.

open as a page

When a business transaction spans multiple services that each have their own local database, why can't a normal ACID transaction hold it together, and how does a saga's use of compensating actions address that?

level: principalimportance: should knowfreq 55%

basics

~20 s

Each service's database can only guarantee atomicity for its own local changes, not across services over a network. A saga breaks the transaction into a sequence of local steps, and if a later step fails, it runs 'undo' actions (compensations) for the steps that already succeeded, instead of a true rollback.

open as a page