UML Class Diagram

Controls.UmlClassDiagram(classes, relations) draws a UML class diagram. Each class is a box with a header and an attribute compartment. The header holds an optional «stereotype», the name, and the package of a foreign class. Each attribute line reads name : Type [multiplicity].

Classes are joined by relations:

Relation Notation
Association open arrowhead, role name and multiplicity
Composition filled diamond at the whole
Generalization hollow triangle at the base class

A box with an Href is a link.

var (classes, relations) = UmlSchemaProjection.FromContentSchemas(
    typePaths.Select(p => new UmlSchemaInput(p, schemaOf(p))),
    path => $"/DataModelling/Explorer/Type/{Uri.EscapeDataString(path)}");
return Controls.UmlClassDiagram(classes, relations).WithMaxAttributes(6);

The model is the schemas

UmlSchemaProjection.FromContentSchemas reads the JSON schemas @{type}/schema/ publishes:

The projection needs no CLR type and no instance. It is the same answer the schema read gives an agent.

The geometry is pure

UmlClassDiagramControl.Layout is a layered (Sugiyama-style) layout, unit-tested in UmlClassDiagramTest:

Renderers only draw what Layout returns. Every view of the same model therefore draws the same picture, and a renderer needs no JavaScript layout engine.

The layout is computed in C# rather than with a bundled JS engine (ELK, dagre) for three reasons:

The platform already ships Mermaid, but it lays out its class diagrams itself and gives a caller no control over notation or interaction.

Size

WithMaxAttributes(n) caps the attribute lines per box; the rest read + n more. For a large model, pass the classes the page is about plus their direct neighbours rather than the whole package.

Open vocabularies (string constants, never enums): UmlClassKind (local, foreign, featured) and UmlRelationKind (association, composition, generalization). A renderer draws a value it does not know plainly.