Lesson 4 of 6
Event contracts and versioning
10 min
Listen to this lesson
An event is an interface, even if nobody calls it one. The producer promises a type, a topic, an envelope and a payload; every consumer builds on that promise. When Kenji's team renamed a field in the custom agreementClosed event, two consumers broke in CMQ, and nobody could say in advance who would be affected. An event contract makes the promise explicit, versioned and testable.
What an event contract contains
| Part | Contoso's agreementClosed v1 | Where it is defined |
|---|---|---|
| Type | cmc.o2c.coreagreement.Closed.v1 (CloudEvents type) | Contract document; producer code |
| Topic | cmc/o2c/coreagreement/Closed/v1/{companyCode}/{agreement} | Topic design (lesson 3) |
| Envelope | CloudEvents 1.0: specversion, type, source, id, time, datacontenttype | CloudEvents specification |
| Payload schema | Agreement number, customer, company code, closing date, deposit total | JSON Schema in an AsyncAPI document |
| Semantics | Sent once per agreement closed, after commit; notification plus key data | Contract document |
| Owner and consumers | Kenji's team; refund handler, warranty app, analytics | Event catalog |
The parts of an event contract, with Contoso's custom agreement event as the example (illustrative names).
For SAP's standard events, most of this already exists: the SAP Business Accelerator Hub documents S/4HANA business events with their payloads and offers AsyncAPI specifications to download. For custom events, Contoso writes its own AsyncAPI document and stores it with the code. In advanced event mesh, Event Portal serves as the catalog: architects and developers catalog events, schemas and the applications that publish and subscribe to them, so the question "who consumes agreementClosed?" has an answer.
Compatible and breaking changes
| Change | Compatible? | What to do |
|---|---|---|
| Add an optional field | Usually yes, if consumers ignore unknown fields | Same version; tell consumers; test a tolerant reader |
| Add a required field | Yes for consumers, if producers always send it | Same version if all producers are updated together |
| Rename or remove a field | No | New version (v2); publish v1 and v2 in parallel |
| Change a field's type or meaning | No | New version, even if the name stays |
| Change when the event is sent | No (semantic change) | New version or new event type |
| Change the topic hierarchy | No for subscribers | New topic version; dual publish during transition |
Rules Theo applies to every event change. A change is breaking if any consumer could behave differently.
Walkthrough: versioning agreementClosed
- The business wants the deposit total split into refunded and retained amounts, replacing one field with two. That is a breaking change.
- Kenji's team defines
cmc.o2c.coreagreement.Closed.v2with a new schema and the topic levelv2, documents it in AsyncAPI and in the catalog. - The producer publishes both v1 and v2 for every closed agreement during the transition.
- Each consumer team moves its subscription from
.../Closed/v1/>to.../Closed/v2/>in its own release, after its tests pass on v2. - The catalog shows v1 consumers dropping to zero; Theo announces the v1 end date and then stops publishing v1.
Data events and sensitive data
Most SAP standard events are notifications with keys. Custom events are where teams are tempted to add data: names, addresses, amounts. Every field added to an event is copied to every subscriber's queue and logs, so Nadia, the security and GRC lead, reviews payloads that carry personal or financial data. Contoso's rule is to send keys plus the minimum data consumers cannot reasonably fetch, and to let consumers read the rest through APIs with their own authorizations. The deposit total in agreementClosed passed that review; the customer's contact person did not.
Contract tests
Contract tests catch breaking changes before they reach CMQ. On the producer side, a test builds the event from known agreement data and validates it against the published schema: if the schema says depositTotal is required and a developer renames it, the build fails. On the consumer side, each consumer keeps sample events for every version it supports and runs them through its parser and logic. Contoso stores the samples in the same repository as the AsyncAPI document, so both sides test against one source of truth.
- Producer buildEvent built from test data validates against the schema of its version
- Consumer buildSample events for each supported version pass parser and logic tests
- Catalog reviewBreaking change without a new version is rejected in review
- CMQ integrationReal events from CMQ client 100 reach each subscribed consumer
- ReleaseCatalog updated: versions, consumers, deprecation dates
What goes wrong in testing
- Silent breaking change. A field rename shipped under the same version breaks consumers in production.
- Strict readers. A consumer that rejects any unknown field turns every compatible change into an outage.
- No catalog. The producer cannot know who to notify.
- Forever-dual publishing. v1 is never retired, and every change must be made twice.
What would you do?
Kenji's team wants to rename customer to soldToParty in agreementClosed v1 because "it is only a name". Two consumers read customer today. What should Theo require?
- An event contract covers type, topic, envelope, payload schema, semantics, owner and consumers.
- Use the Business Accelerator Hub for SAP events and AsyncAPI documents for custom events; Event Portal can catalog both.
- Renames, removals, type or meaning changes are breaking: new version, dual publish, retire.
- Contract tests on both sides catch breaking changes before CMQ.
Check your understanding · not graded
Lesson check
1. Which change to an event is breaking?
2. Where can teams find AsyncAPI documents for S/4HANA business events?
3. What does dual publishing during a transition mean?
4. What does a producer-side contract test check?
5. When can v1 of an event be retired?