Introduction
Pragmatic Design is a .NET meta-framework powered by Source Generators. You declare your architecture with attributes and partial classes. At compile time, a unified generator detects what you wrote and emits the plumbing: repositories, endpoints, validators, mappers, DI registration, metadata. Zero reflection, zero runtime overhead, AOT-ready.
The problem
Section titled “The problem”Every .NET service ends up re-implementing the same scaffolding:
- repository classes that wrap
DbContext<T> - endpoint registration and request binding
- validation pipelines, authorization filters
- DI
AddScopedlists that drift out of sync - entity configuration, audit fields, soft-delete
- outbox, saga, retry, cache-key generation
- cross-module glue that silently rots
This code is repetitive, error-prone, easy to miss in review, and it isn’t yours — it’s infrastructure. Hand-writing it again for every service is the worst kind of friction: it slows the first week and keeps slowing you forever.
The approach
Section titled “The approach”You write the declaration:
[Entity<Guid>, Auditable, SoftDelete][BelongsTo<ReservationsBoundary>]public partial class Reservation{ [LogicKey] public string Code { get; private set; } = ""; public DateTime CheckIn { get; private set; } public DateTime CheckOut { get; private set; }}
[DomainAction, BelongsTo<ReservationsBoundary>][Endpoint(HttpVerb.Post, "/reservations")][Validate]public partial class CreateReservation : IDomainAction<ReservationResult>{ public required DateTime CheckIn { get; init; } public required DateTime CheckOut { get; init; } public required string GuestId { get; init; }}The generator forges everything else:
Reservation.Create(...)factory with audit fields and soft-deleteIRepository<Reservation, Guid>with CRUD + logic-key lookup- EF Core entity configuration for
Reservation CreateReservationInvokerwith DI resolution, validation, authorization, logging, telemetry- HTTP endpoint registration at
POST /reservationswith strongly-typed request/response - Metadata aggregation into the host so everything wires up on startup
You can read every generated file — it lives in obj/Generated/. No bytecode manipulation, no magic runtime behaviour.
Principles
Section titled “Principles”- Source Generator First — zero reflection, AOT-ready, errors at compile time, not at first request.
- Composition by presence — add a NuGet, the generator detects it and wires up. Remove it, the generated code vanishes. No feature flags, no
ifstatements in config. - Host decides — your app’s
.csprojis the source of truth for what’s active. Module strategy (IPragmaticBuilder.Use*()) inProgram.cspicks implementations. - Default always works — every module ships a working default (in-memory store, allow-all policy, passthrough cache). You override to go to production.
- Result over exceptions — domain errors are values, not throws. See
Pragmatic.Result. - No hidden reflection — if you think you need
typeof(T).GetProperties(), ask the generator to produce the accessor instead.
Architecture at a glance
Section titled “Architecture at a glance” ┌─────────────────────┐ │ Product / App │ Showcase, your services ├─────────────────────┤ │ Medium Block │ Identity.Local, Comments, Tags, Attachments ├─────────────────────┤ │ Building Block │ Result, Actions, Persistence, Messaging, … └─────────────────────┘| Tier | Scope | Examples |
|---|---|---|
| Building Block | Toolkit infrastructure. You use it to build your domain. | Result, Actions, Persistence, Composition, Events, Messaging, Jobs, Migrations |
| Medium Block | Cross-cutting packages that ship complete. Include, configure, use. | Identity.Local, Comments, Tags, Attachments |
| Product / App | Composition of blocks into a running application. | Showcase (E2E reference), your own services |
See Architecture for the layering and how modules compose.
Who it’s for
Section titled “Who it’s for”You probably want Pragmatic Design if:
- you ship .NET services in production and spend energy fighting boilerplate
- you value explicit, inspectable code over framework magic
- you want AOT compilation as a real option, not an aspiration
- you’ve used MediatR / MassTransit / Hangfire / AutoMapper / FluentValidation and like some, but want them composed instead of stitched together
You probably don’t want it (yet) if:
- you need a framework that hides all the plumbing behind a single decorator
- you depend on ecosystems (OpenTelemetry ingest, Stripe bindings) that aren’t first-class yet — several are planned
- you can’t run .NET 10 preview or later
Status
Section titled “Status”The current public preview is 0.8. Forty modules ship as NuGet packages, an end-to-end Showcase application demonstrates how they compose, and 42 console / web samples are available to run with dotnet run. APIs are stabilising but may still change before 1.0.
Next steps
Section titled “Next steps”- Installation — get it building in minutes
- Architecture — 3-tier model, module layers, Medium Blocks
- How the SG works — the one unified generator
- Showcase walkthrough — end-to-end reference app