Nov 6, 20266 min read

Orchestration vs Choreography in Microservices

Orchestration puts a central coordinator in charge of a multi-step process. Choreography lets services react to events and drive the next step themselves. When each pattern fits, and how Encore Pub/Sub supports event choreography without inventing a workflow engine.

Orchestration and choreography are two ways to run a business process across microservices. In orchestration, a central component calls each step and owns the branching. In choreography, services emit events and other services decide what to do next, so there is no single conductor process.

Neither pattern is automatically "more microservices." A chatty orchestrator can couple everything to one deployment unit; an unbounded event web can hide the actual sequence until an outage. This guide separates the tradeoffs and shows how Encore Pub/Sub implements the choreography side without pretending to be a durable workflow engine.

Orchestration vs choreography at a glance

OrchestrationChoreography
Who decides the next step?A central orchestratorEach consuming service
CouplingServices couple to the orchestrator APIServices couple to event contracts
VisibilityOne timeline in the orchestratorSpread across logs, traces and subscriptions
Failure handlingOrchestrator retries or compensatesPer-subscriber retries, DLQs, local status
Best fitComplex branching, explicit sagas, auditsFan-out, independent reactions, evolving consumers

How orchestration works

An orchestrator (a dedicated service, a state machine or a workflow worker) receives "start order," then calls inventory, payment and shipping in sequence or in a defined parallel graph. It stores where the process is, and compensation logic ("cancel hold," "refund") lives next to that state.

Strengths: one place to read the happy path, easier timeouts and human approvals when the engine supports them, clearer ownership of "the order process." Costs: the orchestrator becomes a dependency for every step, versioning in-flight runs is your problem (or your workflow engine's), and scaling the conductor is separate from scaling the workers.

AWS Step Functions and Temporal are common orchestrators when you need a durable control plane. They are not required for every multi-step API; a single service calling two clients with a database row is still orchestration, just without a product name.

How choreography works

In choreography, the order service commits "OrderPlaced" and publishes an event. Inventory, email and analytics each subscribe. The order service does not call them and does not know how many consumers exist. New consumers appear by adding a subscription.

Strengths: producers stay stable as consumers grow, blast radius of a slow consumer is its own backlog, and teams can ship reactions independently. Costs: the end-to-end sequence lives in the event graph, debugging needs correlation IDs and traces, and an "exactly once" business action requires idempotent handlers under at-least-once delivery.

Amazon SNS and Amazon SQS are a typical cloud implementation: a topic fans out, queues buffer each consumer, dead-letter queues hold poison messages. The application still owns schema versioning and idempotency.

Choosing without dogma

Pick orchestration when one process must enforce a strict sequence, compensate across steps or show a single status API that is the source of truth for the whole run.

Pick choreography when the primary need is fan-out of a domain fact to independent policies (notify, index, bill, train a model) and each consumer can own its failures.

Many systems mix both: a small orchestrator inside a bounded context, and choreography across contexts via events. Dual-write from a database transaction to a bus without an outbox is a common choreography bug; publish only after commit, or use an outbox, so consumers never see events for rolled-back rows.

Event choreography with Encore Pub/Sub

Encore

Encore works alongside the service layout you already have, and declared topics and subscriptions are infrastructure from code. The platform provisions and deploys into your own AWS or GCP account: you declare topics and subscriptions in application code, and Encore provisions the messaging infrastructure in your own cloud on deploy.

Encore does not act as a workflow engine and does not store Temporal-style event history or Step Functions state machines. For choreography, the Pub/Sub APIs give you typed topics, subscriptions, at-least-once or exactly-once delivery settings, retries and dead-letter queues. On AWS that becomes SNS plus SQS with DLQs and an IAM role for the service, per the AWS infrastructure docs. When you need durable orchestration with timers and signals, run Temporal or Step Functions beside the app; when you need fan-out and independent consumers, declare Pub/Sub in Encore.

The Infrastructure page in the Encore dashboard with a database's configuration panel open, showing fields for vCPUs, memory, storage, availability type, max connections and backups.
Each provisioned resource can be reviewed and its configuration edited per environment in the Encore dashboard. The screenshot shows a GCP Cloud SQL database.

An order placement that fans out to email and fulfillment:

// order/order.ts import { api } from "encore.dev/api"; import { Topic } from "encore.dev/pubsub"; import { SQLDatabase } from "encore.dev/storage/sqldb"; const db = new SQLDatabase("order", { migrations: "./migrations" }); export interface OrderPlaced { orderId: string; customerId: string; } export const orderPlaced = new Topic<OrderPlaced>("order-placed", { deliveryGuarantee: "at-least-once", }); export const placeOrder = api( { expose: true, method: "POST", path: "/orders" }, async ({ customerId }: { customerId: string }): Promise<{ orderId: string }> => { const orderId = crypto.randomUUID(); // Persist the order first; publish only after the insert has committed. await db.exec` INSERT INTO orders (id, customer_id) VALUES (${orderId}, ${customerId})`; // If the publish can fail after the commit, relay through an outbox table instead. await orderPlaced.publish({ orderId, customerId }); return { orderId }; }, );

A second service (or package) subscribes independently; the producer does not import it:

// notify/welcome.ts import { Subscription } from "encore.dev/pubsub"; import { orderPlaced } from "../order/order"; const _ = new Subscription(orderPlaced, "send-order-email", { handler: async (event) => { // Idempotent send keyed by event.orderId }, });

encore run delivers Pub/Sub locally so both services can be tested without AWS. The same pattern works from the Go Pub/Sub docs. Add object storage, cron or secrets in the same repo when a consumer needs them; vary the resources to the job instead of only queues. An AI coding agent can add a topic and subscription in one pull request and verify the message path under encore run before the deploy creates the cloud resources.

Best fit: Teams that want event choreography as infrastructure from code in TypeScript or Go on their own AWS or GCP account, and who will adopt a dedicated workflow engine only for processes that need durable orchestration.

When to pick which

If you need...Pick
Fan-out to independent consumers, evolving subscribersChoreography (Pub/Sub + Encore)
One visible multi-step process with compensationsOrchestration (service or workflow engine)
Waits measured in days, signals, exact resume of code stateTemporal or Step Functions
Periodic sync on a clockCron jobs, not a choreography debate

Frequently asked questions

What is the difference between orchestration and choreography?

Orchestration uses a central component that calls services in order and decides what happens next. Choreography has no central conductor: each service publishes and consumes events, and the sequence emerges from those reactions. Orchestration is easier to follow in one place; choreography scales fan-out with looser coupling.

When should I use choreography instead of orchestration?

Prefer choreography when many services must react independently to the same fact (signup, payment captured, order shipped), when you want producers unaware of every consumer, and when at-least-once messaging with idempotent handlers is enough. Prefer an orchestrator when one process owns branching, compensation and a single visible timeline.

Is Pub/Sub the same as a workflow engine?

Pub/Sub delivers events to subscribers with retries and dead-letter queues, while a workflow engine such as Temporal stores durable control state, timers and signals so a process can resume after days. Many backends only need Pub/Sub plus a status table; reach for a workflow engine when you need that durable control plane.

How do I implement event choreography with Encore?

Declare a Pub/Sub topic and one subscription per reacting service in TypeScript or Go. Publish after the state change commits, keep handlers idempotent, and deploy: on AWS Encore provisions SNS and SQS (with dead-letter queues) and an IAM role for the service. encore run delivers messages locally so you can test the path before cloud deploy.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders