Skip to content

Consuming Generated Docs

The source generator emits living documentation as compile-time const strings — a glossary, a C4 container diagram, and an AsyncAPI event contract. They are always in sync with the code (no annotations, no drift) and cost nothing at runtime. This guide shows how to surface them.

Each is generated per assembly that contains the relevant declarations, in the {AssemblyName}.Generated namespace:

ConstantGenerated inFromContent
PragmaticGlossary.Markdownevery assembly with [Entity] typesentities + their XML-doc summariesMarkdown glossary grouped by namespace
PragmaticArchitecture.C4ContainerDiagramthe host (has [Include<…>] / [RemoteBoundary<…>])the module topologyMermaid container diagram (remote boundaries marked)
PragmaticAsyncApi.Jsonevery assembly with IDomainEvent typesdomain events + payload schemaAsyncAPI 3.0 document
PragmaticSagaDiagrams.{Saga}every assembly with [Saga<TState>] classes (namespace Pragmatic.Messaging.Generated)the saga state machineMermaid stateDiagram-v2 per saga (timeouts, compensations, terminal states)

Because they are per-assembly, you reference the constant on the assembly you care about — e.g. the glossary of the Catalog boundary is Catalog.Generated.PragmaticGlossary.Markdown, the C4 diagram of the host is MyApp.Host.Generated.PragmaticArchitecture.C4ContainerDiagram.

They are plain strings, so a minimal-API endpoint is all you need:

// Architecture (Mermaid) — render it in a docs page or paste into mermaid.live
app.MapGet("/docs/architecture.mmd", () =>
Results.Text(MyApp.Host.Generated.PragmaticArchitecture.C4ContainerDiagram, "text/plain"));
// Event contract (AsyncAPI 3.0) — point an AsyncAPI Studio / Microcks at it
app.MapGet("/asyncapi.json", () =>
Results.Text(Catalog.Generated.PragmaticAsyncApi.Json, "application/json"));
// Ubiquitous-language glossary (Markdown)
app.MapGet("/docs/glossary.md", () =>
Results.Text(Catalog.Generated.PragmaticGlossary.Markdown, "text/markdown"));

Gate them behind an admin policy if you don’t want them public:

app.MapGet("/asyncapi.json", () => Results.Text(Catalog.Generated.PragmaticAsyncApi.Json, "application/json"))
.RequireAuthorization("Admin");

Each boundary emits its own constant. To present one document, concatenate or merge at startup:

// One combined glossary across boundaries
var glossary = string.Join("\n\n",
Catalog.Generated.PragmaticGlossary.Markdown,
Booking.Generated.PragmaticGlossary.Markdown);
app.MapGet("/docs/glossary.md", () => Results.Text(glossary, "text/markdown"));

The C4 diagram is already whole-system (generated on the host from the composed modules), so no aggregation is needed for it.

The AsyncAPI document carries each event’s payload schema, so snapshotting it turns “did an event’s shape change?” into a build-time check. With Verify:

[Fact]
public Task EventContract_IsStable()
=> Verify(Catalog.Generated.PragmaticAsyncApi.Json);

A reviewed change to a public event shows up as a snapshot diff; an unintended breaking change fails the test. Events are tagged in the document:

  • x-pragmatic-public: true — a public integration event (IIntegrationEvent / [PublicEvent]), the cross-boundary contract. Evolve these additively.
  • x-pragmatic-obsolete: true — deprecated ([ObsoleteEvent]); remove once consumers have migrated.

See Events — Integration events for the two-level event model.