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.
What gets generated
Section titled “What gets generated”Each is generated per assembly that contains the relevant declarations, in the
{AssemblyName}.Generated namespace:
| Constant | Generated in | From | Content |
|---|---|---|---|
PragmaticGlossary.Markdown | every assembly with [Entity] types | entities + their XML-doc summaries | Markdown glossary grouped by namespace |
PragmaticArchitecture.C4ContainerDiagram | the host (has [Include<…>] / [RemoteBoundary<…>]) | the module topology | Mermaid container diagram (remote boundaries marked) |
PragmaticAsyncApi.Json | every assembly with IDomainEvent types | domain events + payload schema | AsyncAPI 3.0 document |
PragmaticSagaDiagrams.{Saga} | every assembly with [Saga<TState>] classes (namespace Pragmatic.Messaging.Generated) | the saga state machine | Mermaid 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
Catalogboundary isCatalog.Generated.PragmaticGlossary.Markdown, the C4 diagram of the host isMyApp.Host.Generated.PragmaticArchitecture.C4ContainerDiagram.
Serve them from endpoints
Section titled “Serve them from endpoints”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.liveapp.MapGet("/docs/architecture.mmd", () => Results.Text(MyApp.Host.Generated.PragmaticArchitecture.C4ContainerDiagram, "text/plain"));
// Event contract (AsyncAPI 3.0) — point an AsyncAPI Studio / Microcks at itapp.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");Aggregating across boundaries
Section titled “Aggregating across boundaries”Each boundary emits its own constant. To present one document, concatenate or merge at startup:
// One combined glossary across boundariesvar 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.
Use the AsyncAPI as a contract test
Section titled “Use the AsyncAPI as a contract test”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.