Skip to content

Cross-service contracts

When two services produce and consume the same message, the contract registry enforces field-shape agreement across language boundaries with case-tolerant validation.

Producer service

"produces_contracts": [{
  "name": "events.raw",
  "transport": "kafka:events.raw",
  "fields": [
    {"name": "id", "type": "str"},
    {"name": "received_at", "type": "str"},
    {"name": "headers", "type": "dict[str, str]"},
    {"name": "payload", "type": "str"}
  ]
}]

Consumer service

"consumes_contracts": [{"contract_name": "events.raw", "role": "consumes"}]

How the registry works

  1. The producer's run writes its declaration to disk (the contract registry).
  2. The consumer's run resolves the named contract and validates that its ConsumedEvent entity carries the same field names verbatim.
  3. Validation is case-tolerant across language boundaries: Java's receivedAt matches Python's received_at (both normalize to receivedat).
  4. A renamed field (e.g. body vs payload) still fails — case-tolerance is for naming-convention drift, not semantic drift.

Multi-language example

# Run producer in Python:
squeaky generate --problem-file producer_problem.json
# Run consumer in Java against the same contract:
squeaky generate --problem-file consumer_problem.json

Both projects' generated code reference the same contract; the Java consumer's ConsumedEvent carries the producer's field names verbatim (with Java's idiomatic camelCase normalized for matching).

See also