Event Boundaries, Ownership, and Publication Responsibility

LESSON

Event-Driven Architecture and Streaming Foundations

004 30 min intermediate

Event Boundaries, Ownership, and Publication Responsibility

By the end of this lesson, you will be able to...

  • Decide which service is allowed to publish a domain fact.

  • Review whether an event is published after the owner has committed the state that makes it true.

  • Separate owned facts, derived facts, and commands when an event boundary is ambiguous.

Idea in one sentence: A service should publish only the facts it owns, and only after its own state makes the fact true.

Core Insight

Imagine a checkout team wants other systems to react when an order is ready for fulfillment.

They propose this event:

{
  "type": "OrderReadyForFulfillment",
  "order_id": "O-812"
}

At first, it sounds useful. The warehouse can pick items. Email can tell the customer. Analytics can count ready orders.

But one question decides whether this is a good event:

Who is allowed to say this fact is true?

The order service knows that an order was placed. It may not know that payment was authorized. It may not know that stock was reserved. If it publishes OrderReadyForFulfillment too early, the event name promises more than the publisher owns.

The core design rule is simple:

An event boundary should follow ownership.
A service publishes a fact after it changes or verifies the state it owns.

This does not mean one service can never combine information from several services. It means the combined fact needs an owner too. If nobody owns the combined fact, the event is probably a wish, a command, or a hidden workflow.

The Small Situation

Our checkout system has four services:

Order service
Payment service
Inventory service
Fulfillment service

The user places order O-812.

Three facts may become true:

OrderPlaced
PaymentAuthorized
StockReserved

They do not belong to the same service.

Fact Natural owner Why
OrderPlaced Order service It owns the order record and order lifecycle.
PaymentAuthorized Payment service It talks to the payment provider and records authorization state.
StockReserved Inventory service It owns stock counts and reservation state.

Now the product team wants fulfillment to start only when all three are true.

The naive design is:

Order service publishes OrderReadyForFulfillment

That is tempting because it gives every consumer one clean event. It is also dangerous if the order service does not own the conditions behind the event.

Plain Meaning, Then Precise Meaning

Plain meaning:

An event boundary is the line where one owner says, "This fact is now true, and other systems may react to it."

In this scenario:

The order service can draw the boundary around OrderPlaced. The payment service can draw the boundary around PaymentAuthorized. The inventory service can draw the boundary around StockReserved.

Technical name:

An event boundary is the ownership and publication boundary for a domain fact or integration event. It answers who can name the fact, when the fact becomes true, and what state change makes publication legitimate.

Plain meaning:

Publication responsibility is the duty to publish the fact at the right time and with a truthful name.

In this scenario:

The order service is responsible for publishing OrderPlaced after it commits the order. It is not automatically responsible for publishing PaymentAuthorized, because payment state belongs to another service.

Technical name:

Publication responsibility is the producer-side contract for an event. It includes the source of truth, the commit point, the event identity, the schema, and the operational signal that tells us publication is failing.

The Naive Design

Here is the first version.

1. Order service creates O-812.
2. Order service calls payment.
3. Order service calls inventory.
4. Order service publishes OrderReadyForFulfillment.

The design looks convenient because one service coordinates the whole story.

But look at what the event says:

OrderReadyForFulfillment

That name is not only about the order. It also says payment and inventory are ready. If the order service does not store the authoritative payment state and inventory reservation state, then it is publishing a fact it only inferred from calls to other services.

That inference can be wrong.

Payment may authorize the card, then later reverse or expire the authorization. Inventory may reserve stock, then fail to commit the reservation. The order service may receive a timeout after the payment provider actually authorized the card.

Now the event has a trust problem:

consumer sees OrderReadyForFulfillment
consumer assumes the fact is true
producer may only know "I think the calls worked"

This is where vague event boundaries become production bugs.

Where The Naive Design Breaks

Suppose this timeline happens:

Time Order service Payment service Inventory service Event system
T0 Creates O-812. No payment state. No reservation state. No event.
T1 Calls payment. Authorizes payment but response times out. No reservation state. No event.
T2 Retries payment call. Rejects duplicate or returns unknown. No reservation state. No event.
T3 Calls inventory. Payment state is still uncertain to order. Reserves stock. No event.
T4 Publishes OrderReadyForFulfillment. Payment may be authorized, duplicate, or unknown. Stock is reserved. Holds ready event.

Input:

The order service wants one clean event for fulfillment.

Transition:

It combines several remote observations and publishes OrderReadyForFulfillment.

Intermediate state:

Payment truth lives in payment. Inventory truth lives in inventory. The order service has partial, timing-sensitive knowledge.

Output or decision:

Fulfillment receives a ready event whose name may be stronger than the producer's knowledge.

Naive failure contrast:

The naive design treats "the order service tried the required calls" as the same as "the required business facts are true." Those are not the same state.

The safer design is to publish smaller owned facts:

Order service     -> OrderPlaced
Payment service   -> PaymentAuthorized
Inventory service -> StockReserved

Then one of two things can happen:

  1. Fulfillment, or a small process component, waits until it has seen the required facts and then requests fulfillment.
  2. A process-owning service records the readiness decision and publishes OrderReadyForFulfillment only after it owns that decision.

The second option is not free. It creates a real owner for the combined fact. That owner must store process state, handle retries, and expose operational signals. Deeper saga and workflow patterns live in later tracks, but the boundary rule already applies here: if a fact combines several owners, the combined fact needs its own owner.

Check: Can the order service publish PaymentAuthorized because it sent the payment request?

Think first, then reveal.

Answer: No. Sending a request is not the same as owning the resulting fact. The payment service owns PaymentAuthorized because it talks to the payment provider and records the authorization state.

A Publication Responsibility Review

Use this review when a proposed event feels ambiguous.

Review question Good answer Warning sign
What fact does the event name? A fact that is already true. A wish, instruction, or future hope.
Who owns the source state? One service can point to its committed state. The fact depends on scattered remote observations.
When is the event published? After the owner commits the state change. Before the state change, or while it is still uncertain.
What can consumers assume? They can trust the named fact, not every downstream side effect. They must guess which parts are actually true.
What signal shows publication is failing? Outbox backlog, publish failure, missing event for committed state. No owner can tell whether the event was missed.

Apply it to OrderPlaced.

Question Answer
What fact? An order exists in placed state.
Who owns it? Order service.
When publish? After the order record is committed, or from an outbox tied to that commit.
What can consumers assume? The order was placed. They cannot assume payment, stock, email, or fulfillment succeeded.
Failure signal? Orders in placed state with no matching event, or outbox entries not published.

Apply it to OrderReadyForFulfillment.

Question Answer
What fact? The order has satisfied the readiness policy for fulfillment.
Who owns it? Not automatically the order service. It needs a process owner or fulfillment owner.
When publish? After that owner records the readiness decision from the required facts.
What can consumers assume? The readiness decision was made under the published policy.
Failure signal? Process state stuck waiting for payment, inventory, or a publish attempt.

This review does not choose a broker topology yet. That is the next lesson. First decide whether the event deserves to exist and who can tell the truth about it.

Commands, Facts, And Derived Facts

Ambiguous boundaries often come from mixing three shapes.

Owned fact

OrderPlaced
PaymentAuthorized
StockReserved

An owned fact says, "This owner changed or verified its own state."

Use an event when other systems can react independently and do not need an immediate answer to complete the original request.

Command

ReserveStock
CapturePayment
StartFulfillment

A command says, "Please do this."

Use a command when one component is asking another component to make a decision or perform an action. Commands can move through message infrastructure, but they are still commands. Do not rename them as events to make the system look more decoupled.

Derived fact

OrderReadyForFulfillment

A derived fact says, "A policy evaluated several facts and recorded a new conclusion."

Use a derived event only when some owner records that conclusion. The owner may be a process manager, a fulfillment service, or another component with explicit responsibility for the readiness policy.

Check: A marketing service wants to publish CustomerIsHighValue after reading orders, returns, and support tickets from several systems. What is the first boundary question?

Think first, then reveal.

Answer: Ask who owns the high-value decision and where that decision is stored. If marketing owns a scoring policy and records the score, it may publish CustomerClassifiedAsHighValue. If it only noticed a few events and guessed, the event name is too strong.

Trade-offs And Limits

Ownership-based publication improves trust. Consumers can treat an event name as a real fact, not as a producer's hope.

It costs design work. Teams must agree which service owns which state, what event names promise, and which component owns derived decisions.

It can still fail. A service can commit state and fail before publishing. That is why later lessons introduce outbox and inbox patterns. A service can also publish a truthful fact with a schema that consumers misunderstand. That is why contract evolution gets its own lesson.

It does not solve cross-service invariants by itself. If payment, inventory, and fulfillment must move through a long-running business process, you need a process design. This lesson only gives the first review question: which owner is allowed to publish which fact?

You can see the boundary when:

The trade-off is healthy: smaller owned facts create more events, but each event is easier to trust, replay, and review.

Common Confusions

Confusion: "The service that needs the event should publish it"

Why it is tempting:

Fulfillment needs to know when an order is ready, so it feels natural to let the order service publish the readiness event for fulfillment.

Better model:

The service that owns the fact should publish it. Need is not ownership. Fulfillment may need PaymentAuthorized, but payment owns that fact.

Confusion: "If the event is useful, the boundary is good"

Why it is tempting:

A single useful event can simplify many consumers.

Better model:

Usefulness is not enough. A useful event with unclear ownership becomes a shared rumor. A good event is both useful and truthfully owned.

Confusion: "A derived event is always bad"

Why it is tempting:

Derived events can hide multi-service logic, so teams may reject them completely.

Better model:

A derived event is valid when an owner records the derived decision. The problem is not derivation. The problem is publishing a derived fact without owning the policy and state behind it.

Practice

Review this proposed event:

{
  "type": "SubscriptionActivated",
  "subscription_id": "S-44",
  "customer_id": "C-18"
}

Context:

Billing service owns payment method checks.
Plan service owns plan eligibility.
Subscription service owns subscription lifecycle.
Email service sends welcome messages.

The subscription service wants to publish SubscriptionActivated immediately after the user clicks "Start subscription," before billing and plan eligibility have finished.

Answer these questions:

Model answer:

SubscriptionActivated names a lifecycle fact: the subscription is active. The subscription service can own that fact if it records active state only after required billing and plan checks are satisfied. It should not publish the event immediately after the click, because the click is closer to a command such as StartSubscription or RequestSubscriptionActivation.

Smaller facts may include PaymentMethodAccepted from billing and PlanEligibilityConfirmed from the plan service. The subscription service may consume those facts, record ACTIVE, and then publish SubscriptionActivated. A useful signal is subscriptions in active state with no matching event, or an outbox backlog for subscription events.

Resources

Key Takeaways

PREVIOUS Producers, Consumers, Brokers, and Event Logs NEXT Queues, Topics, Pub/Sub, and Log Topologies