Cross-domain delivery
document:docs/cross-domain-deliveryWhat autocatalog enforces when work crosses a domain boundary, step by step, and what it leaves to people.
A dependency between domains should create a contract, not shared ownership. When work crosses a boundary, the teams agree the interaction first and build against it in parallel. This page maps each step of that to what autocatalog checks, so the agreement is enforced in CI rather than kept on a wiki.
Some steps are decisions only people can make. They are listed as such, and nothing here pretends otherwise.
1. Know who owns it
Section titled “1. Know who owns it”Every event, table and service carries an owner, read from its manifest,
catalog-info.yaml, a Datadog import or CODEOWNERS. A repository that tries to
redefine a resource another repository declares is quarantined and named, and
the owners page lists what each team owns.
ac health --min 70 # an event with no owner loses points2. Describe the capability, not the implementation
Section titled “2. Describe the capability, not the implementation”A people decision. What autocatalog does is catch the commonest leak: a service that starts reading another team’s table fails the pull request.
checkout (payments) reads table finance/eligibility, which finance owns3. Choose the interaction
Section titled “3. Choose the interaction”Events, commands (one handler, an expected outcome), webhooks, endpoints and
queues are all catalogued. Choosing between them stays with the teams, and the
gate keeps each one what it says it is: an event two services publish is
suspicious, and a new event that does not read as something that happened, or
a command that does, is noted. A command whose sender waits for the result
names the queue the handler replies on with outcome.replyTo.
Direct database access across domains is the one choice that is enforced: see
another team’s datastore. A deliberate
exception, such as a migration, is reviewed with ac resources promote.
4. Define the contract first
Section titled “4. Define the contract first”Write it as Zod, Pydantic, JSON Schema, OpenAPI, AsyncAPI, Avro or protobuf; all of them become one versioned, validated contract. What arrival looks like is part of it too:
"delivery": { "guarantee": "at-least-once", "orderingKey": "orderId", "idempotencyKey": "orderId", "correlationKey": "meta.traceId", "maxLateness": "PT5M"}ac validate fails a key the schema does not have. See
delivery.
5. Agree examples, not just schemas
Section titled “5. Agree examples, not just schemas”Put each edge case in a folder named for it:
examples/ placed.json the happy path empty/no-lines.json partial/no-address.json duplicate/redelivered.json repeats another example's idempotency key late/after-outage.json invalid/no-total.json must fail the schemaAn invalid example that the schema accepts fails validation: it was meant to
show consumers what to reject. A duplicate that repeats no other example’s
idempotency key is a warning. ac health asks every event for at least one
edge case, and an at-least-once event for a duplicate. EventCatalog renders the
same folders as they are.
6. Build in parallel
Section titled “6. Build in parallel”ac to-asyncapi --service billing --out billing.asyncapi.yamlwrites one service’s view as AsyncAPI 3.0, with every valid example and the
correlation id, for a mock server such as Microcks to serve while the provider
is still being built. Invalid examples are left out, since a mock would serve
them as real messages. The document carries EventCatalog’s
x-eventcatalog-message-* extensions, so EventCatalog’s AsyncAPI generator
reads it, and ac from-asyncapi brings it back without losing anything.
7. Test the boundary automatically
Section titled “7. Test the boundary automatically”ac drift --base-ref origin/mainBreaking changes fail the pull request and name the consumers they reach, narrowed by the fields each consumer says it reads. See drift gating. Consumer-driven pact suites are a different tool, and complement this rather than replace it.
Each side tests against the contract, not against the other side. The
producer’s tests show it publishes what it declares: the snapshot they write
is checked by ac drift --snapshot. The consumer’s tests feed it the event’s
examples, edge cases included, and check it handles them. Neither needs the
other running, or a broker.
8. Preserve ownership
Section titled “8. Preserve ownership”Reading or writing another team’s datastore is enforced (step 2). Copying another domain’s business rules into your own code is not detected: telling a copied rule from a coincidence is guesswork, and a guess in a CI gate is worse than none.
9. Design failure behaviour
Section titled “9. Design failure behaviour”Three of the questions have an answer the contract can hold and CI can check:
duplicates (guarantee and idempotencyKey), order (orderingKey) and
lateness (maxLateness). A consumer declares where it puts a delivery it gives
up on with deadLetter in autocatalog.consumes.json, and the snapshot from
its tests says how many deliveries failed, were retried or were dead-lettered,
and why (delivery outcomes). A
process that has to undo earlier steps when a later one fails names the step
that undoes each one with compensated_by in its
flow. Stale data and whether to fail open or closed are runtime
design, and stay with the team.
10. Make it observable
Section titled “10. Make it observable”ac health asks each event for a correlation field: one under a common name
(correlationId, traceId, traceparent, causationId), one named by
delivery.correlationKey, or a CloudEvents envelope. Traces imported with
ac resources scan --traces add what actually ran to the graph, and the MCP
server’s investigation bundle hands a failing journey to autotel.
11. Define change rules
Section titled “11. Define change rules”The severity model is the change rulebook: adding an optional field is safe, removing one is breaking, and so is a longer maximum lateness or a changed ordering key. A major version acknowledges a breaking change. A change of meaning with no change of shape cannot be seen in a schema; a changed description is reported, for information.
12. Have an escape hatch
Section titled “12. Have an escape hatch”Collaboration while a boundary is still being found is a people matter. The
tools give it an end state: ac resources promote records a reviewed
exception, and a major version records a deliberate break. Both stay visible,
so temporary coupling does not quietly become permanent.