Skip to main content
Welcome. This site supports keyboard navigation and screen readers. Press ? at any time for keyboard shortcuts. Press [ to focus the sidebar, ] to focus the content. High-contrast themes are available via the toolbar.
serard@dev00:~/cv

Summary

Layer What It Defines Who Writes It
M3 Primitives for building DSLs Framework authors (once)
M2 Six CMF DSLs (DDD, Content, Admin, Pages, Workflow, Requirements) DSL authors
M1 Domain models using the DSLs Application developers
M0 Runtime instances The program at runtime

This series and Modeling, Metamodeling, and Meta-Metamodeling in C# form a pair. The first establishes the theory -- M3, attributes as DSL surfaces, multi-staged Roslyn generators. This series demonstrates the application -- a Content Management Framework that turns decorated classes into a full-stack DDD application with pages, workflows, audit, search, caching, and a shared Blazor WebAssembly kernel. The Diem project started this journey in PHP in 2010. The meta-metamodel completes it in C# in 2026.

What the Nineteen Parts Built

In total this series describes a CMF whose surface is six DSLs (extensible to N), whose pipeline is five stages plus reporting, and whose runtime is a Blazor-native full stack with type safety end-to-end. The mental model that links every part is a single sentence: the developer writes attributes, the compiler writes everything else, and the analyzers fail the build when anything drifts.

Part Layer Headline
01 Introduction Context CMF vs CMS, the Diem heritage
02 Architecture Pipeline Two axes, multi-stage Roslyn
03 DDD DSL M2 Aggregates, entities, value objects, commands, sagas
04 Composition M2 Composition vs association, EF Core mapping
05 Admin DSL M2 One attribute, full Blazor CRUD
06 Content Parts M2 Parts, blocks, StreamFields, versioning
07 Pages DSL M2 Page tree, layouts, dynamic routing
08 Workflow DSL M2 Editorial state machines, gates, per-locale
09 Generated output Pipeline Stage 2+3 walkthrough, 1 : 41 ratio
10 Shared kernel Runtime One C# kernel, two compilation targets
11 CLI tooling Surface cmf new, add, generate, validate, report, design
12 E-commerce walkthrough Application Day-by-day from cmf new to running storefront
13 Requirements DSL M2 Features, ACs, gates, traceability
14 Landscape Context Why Diem, Wagtail, Orchard, Drupal, Symfony CMF, Strapi
15 Testing Practice Seven layers from invariants to E2E
16 Security & audit Cross-cutting RBAC, claims, multi-tenancy, audit trail
17 Extending M3 Extension A seventh DSL as a NuGet package
18 Performance Runtime Eager loading, caching, read models, search
19 Summary This page

Two Properties Worth Restating

Two emergent properties of the design are worth pulling out, because they explain why the cost-benefit of all this generator infrastructure is positive:

Single source of truth, propagated mechanically. Any change a developer makes to the M1 model — renaming a property, adding a workflow stage, changing a required role, marking a feature as Done — cascades through the entire generated stack on the next build. There is no second place where the change has to be remembered. The most expensive class of CMS bug, "the database, the API, and the UI disagree about what a thing is", is impossible by construction.

Failure visible at compile time, not in production. The analyzer families CMF1xx through CMF6xx cover aggregate shape, composition cycles, kernel hygiene, requirement coverage, workflow reachability, and performance budgets. Anything that would surprise the developer at 3 a.m. instead surprises them in the IDE squiggle, three seconds after the offending keystroke. The cost is a slower compile; the savings are measured in the absence of incidents.

What the CMF Does Not Cover

The series is a design specification for a content management framework, which is a deliberately bounded scope. The following concerns are outside it and either belong to neighboring DSLs or remain as hand-written code:

Concern Belongs to Notes
Build pipelines, deployment, infrastructure Ops DSL ecosystem The CMF emits artifacts; the Ops DSL ships them
SDLC pipelines as types SDLC DSL (in the broader cmf/ series) Local-first build pipelines
ALM (services, flags, SLOs) ALM DSL Aspire wiring, OpenTelemetry, dashboards
PLM (products, releases, roadmap) PLM DSL Build-time-only metadata
Authentication providers (OIDC, JWT, cookie) Hand-written Startup.cs The CMF generates authorization, not authentication
Distributed transactions across bounded contexts Distributed Task series Sagas with idempotency keys
Long-form editorial workflows that span humans and time The Editorial workflow DSLbounded Year-long approval chains belong elsewhere
Real-time collaboration (operational transforms, CRDTs) Out of scope A deliberate non-goal
Static-site generation Out of scope The CMF is dynamic-first
AI-assisted content generation Out of scope A separate concern with its own lifecycle

The list of non-goals is as important as the list of goals. A framework that promises everything delivers a Drupal — a system whose features are individually adequate and collectively a maintenance burden. The CMF promises exactly six DSLs plus the M3 surface for adding more, and stops there.

Roadmap: Self-Hosting

The series describes a CMF whose own source has not yet been written. The roadmap is short:

  1. Bootstrap. Implement the M3 primitives and the DDD DSL by hand. This is the only code that cannot be generated, because it is the generator.
  2. Self-hosting. Re-express the CMF's own type registry, content blocks, and admin pages using the DSLs. The fixed point — the CMF used to define itself — is the moment the M3 design is validated.
  3. Six DSLs. Implement Content, Admin, Pages, Workflow, Requirements as separate generator packages on top of DDD. Each is a few thousand lines of generator code plus templates.
  4. Cross-cutting. Add the audit, security, performance, and testing scaffolds described in parts 15–18. These are the additive generators that run after the six core ones.
  5. First real application. Port a small Diem instance into the CMF and measure the gap. The migration is the truth-test for the design.

A reasonable estimate for steps 1–3 is the same order of magnitude as a small open-source ORM project — months, not years, for a small team. The gating factor is not LOC but the analyzer coverage and the snapshot tests for the generators, which take longer than the generators themselves. This is the right shape: the framework's quality is measured by the quality of its own test suite, not its feature count.

Learning Resources

For a reader who wants to understand the foundations more deeply or apply the same patterns to a different domain:

Resource Why
Modeling, Metamodeling, and Meta-Metamodeling in C# The M3 theory the entire CMF rests on
DDD as a Modeling Discipline The domain modeling vocabulary used throughout parts 03–04
Feature Tracking: A Type-Safe Requirements Chain The requirements layer in depth (nine-part series)
Entity DSL A standalone DSL for entities and value objects, which informed the DDD DSL design
Builder Pattern The pattern every generated aggregate uses
Result Pattern The error-handling discipline shared by command handlers and validators
Finite State Machines The library powering the workflow and requirement lifecycle state machines
Quality Gates The post-test quality gates the CMF integrates with
Ops DSL Ecosystem The deployment counterpart to the CMF — same pattern, different domain

Closing

The Diem project, in 2010, proved that a Content Management Framework — empty by default, generated from declarations, type-checked at runtime — was a viable alternative to a Content Management System. It was held back by the languages and tools of its day: dynamic PHP, YAML schemas, runtime reflection. Sixteen years of progress in compilers, type systems, and Roslyn-style code generation make the original idea executable in a way it could not have been then. This series is what that execution looks like in design form. The next step is to write it.

⬇ Download