Skip to content

Publish a labelled message's contract in a "contract" header - #97

Merged
AhmadRAbuhussein merged 1 commit into
mainfrom
muhannad/contract-header
Oct 30, 2026
Merged

AhmadRAbuhussein merged 1 commit into
mainfrom
muhannad/contract-header

Conversation

@mmalkhatib

Copy link
Copy Markdown
Contributor

What

When a published message's type carries SW.PrimitiveTypes.ContractAttribute (PrimitiveTypes 10.0.2), e.g. [Contract("orders.msg.placed", Major = 2)], the message gets a header contract: orders.msg.placed@2 (BusOptions.ContractHeaderName). A consumer can then tell which shape the body has, and which major, without relying on the class name. Today a message is identified only by its type name, which is also its routing key.

Backward compatibility

Most applications using the bus will never label a message, so this is built to change nothing for them:

  • No attribute, no header. A type without the attribute publishes exactly as before. So does one with a malformed key (IsWellFormed false): it's left out rather than failing the publish. The body, routing key, exchange and every other header are unchanged.
  • No dependency change. The bus stays on SimplyWorks.PrimitiveTypes 10.0.0 and finds the attribute by its full name with reflection, cached per type. Requiring 10.0.2 would turn into a package-downgrade error (NU1605) for any application that pins 10.0.0 directly. Only the integration test project references 10.0.2.
  • Consumers aren't affected. ConsumerRunner reads only the headers it knows by name (checked on current main and on 10.0.2), so older consumers ignore the new one. The dead-letter copy keeps all headers, so the contract survives into .bad.
  • Internal surface only. The new optional contract parameters are on BasicPublisher, which is internal. The only public addition is the BusOptions.ContractHeaderName constant.
  • Raw publishes (Publish(string messageTypeName, string message)) have no type, so they carry no header.

Tests

  • New ContractHeaderTests against a real broker (Testcontainers):
    • a labelled message carries orders.msg.placed@2;
    • an unlabelled one carries nothing;
    • a malformed key carries nothing.
  • Full integration suite 18/18 and unit tests 1/1 pass.

A message whose type carries SW.PrimitiveTypes.ContractAttribute is published
with a "contract" header holding key@major, so a consumer can tell which shape
the body has without relying on the class name. Types without the attribute,
or with a malformed key, publish exactly as before: no header.

The attribute is found by name, so the bus still depends on PrimitiveTypes
10.0.0 and applications that never label a message don't have to upgrade it
(a raised floor would be a package-downgrade error for anyone pinning 10.0.0).
Consumers only read the headers they know, so older consumers ignore it; the
dead-letter copy keeps it.
@coderabbitai

coderabbitai Bot commented Oct 30, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Central YAML; Path: .coderabbit.yaml
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 51d9d998-bd1f-486c-b3ec-1c1ff6daf070
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@AhmadRAbuhussein
AhmadRAbuhussein merged commit 1736eba into main Oct 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants