to navigate Enter to open "…" all these words ANDOR to combine

Featured work · Open source

The MMCA platform

A production-grade .NET 10 platform that demonstrates modern enterprise architecture end to end: an open-source framework of lockstep-versioned NuGet packages plus real applications that prove the patterns under live traffic. The design goal is one sentence: build the monolith now, extract a service later, without a rewrite.

Cleavestack is the commercial home of MMCA: build as one, ship as many.

17
NuGet packages
111
Architecture Decision Records
124
Architecture fitness tests
3
Reference applications

The framework

MMCA.Common

A NuGet package framework for building modular-monolith applications with DDD, Clean Architecture, and CQRS, and the extension points to extract a module into its own microservice later. Every package releases in lockstep at one version and the set spans every layer; a metapackage installs the core six with a single reference.

  • Shared MMCA.Common.Shared Result pattern, value objects, error handling, DTOs
  • Domain MMCA.Common.Domain Entities, aggregate roots, domain events, specifications
  • Application MMCA.Common.Application CQRS handlers, the decorator pipeline, the module system
  • Infrastructure MMCA.Common.Infrastructure EF Core multi-engine, repositories, caching, outbox, message bus, multi-tenancy, audit trail, job scheduler
  • API & transport MMCA.Common.APIMMCA.Common.Grpc Controllers, middleware, idempotency, JWKS, gRPC contracts
  • UI MMCA.Common.UIMMCA.Common.UI.WebMMCA.Common.UI.Maui Blazor shared components, MudBlazor theme, web and MAUI clients
  • Hosting MMCA.Common.AspireMMCA.Common.Aspire.HostingMMCA.Common.Gateway Aspire service defaults, OpenTelemetry, health checks, broker wiring, YARP gateway composition
  • Testing MMCA.Common.TestingMMCA.Common.Testing.ArchitectureMMCA.Common.Testing.E2EMMCA.Common.Testing.UI Integration bases, architecture rule library, Playwright and bUnit harnesses
  • Metapackage MMCA.Common MMCA.Common: one reference that bundles the six core packages (Shared, Domain, Application, Infrastructure, API, Aspire) so a standard app starts from a single PackageReference

Strict layered core

Shared → Domain → Application → Infrastructure → API/Grpc. The Result pattern carries expected failures, entities are rich aggregates with factory methods, and queries compose from specifications instead of LINQ spaghetti.

CQRS decorator pipeline

Thin command and query handlers wrapped by a Scrutor decorator chain: FeatureGate → Authorization → Logging → Caching → Validating → Timeout → Transactional → Handler. The order is load-bearing and registered once.

Extraction boundaries

Application code talks to abstractions; transport lives at the edge. A transport-agnostic message bus, gRPC contracts, JWKS cross-service auth, and Aspire hosting let a module become a service with no domain rewrite.

Proof, not slideware

Three reference applications

The framework is consumed by real apps. Each one exercises the patterns differently, and the conference app ran a live event on Azure.

Conference

MMCA.ADC

A production-deployed conference platform: four microservices (Identity, Conference, Engagement, Notification) behind a YARP gateway, cross-service JWKS auth, bidirectional gRPC, polyglot persistence, QR badge check-in with a live points leaderboard, and load testing at conference-day scale. It powered the Atlanta Cloud + AI Conference.

E-commerce

MMCA.Store

An e-commerce app refactored from a monolith into three microservices (Catalog, Sales, Identity) with Stripe payments, output caching, permission-based access control, and an accessible Blazor and .NET MAUI UI.

Reference seed

MMCA.Helpdesk

The deliberately minimal seed: a single Tickets module exercised through all five layers. It is the worked companion to the framework's getting-started guide and the easiest entry point for exploring the patterns.

A look inside

From one graph, laptop to cloud

Orchestrated locally with .NET Aspire, then deployed to Azure Container Apps from the same declarative model.

The .NET Aspire dashboard showing the MMCA services, databases, and message broker running locally
The .NET Aspire dashboard: services, databases, and the broker as one orchestrated graph.
The MMCA.Common GitHub repository README
MMCA.Common on GitHub: Apache-2.0 licensed, documented, released in lockstep.

How it holds together

Architectural styles the codebase commits to

The recurring ideas every repo is built on, summarized from the onboarding primer's orientation chapter. Each is taught in full at its first concrete appearance in the reference library.

Domain-Driven Design

Aggregates enforce invariants inside their boundary, value objects model concepts with no identity, domain events announce meaningful state changes, and factory methods return a Result so an invalid entity cannot be constructed.

Clean Architecture

Source dependencies point inward toward a framework-free domain; the application layer defines ports and infrastructure implements the adapters. Enforced twice: a compile-time MSBuild layer guard and shared NetArchTest fitness functions.

CQRS

Commands mutate and return a Result; queries are side-effect-free. Both flow through a decorator pipeline (feature gate, authorization, logging, caching, validation, timeout, transaction), so cross-cutting concerns live in the pipeline, not in each handler.

One shared HTTP middleware pipeline

Every REST and gRPC service host builds its request pipeline from a single call that fixes the middleware order once, with the load-bearing adjacencies commented in code. Hosts differ by configuration, never by pipeline shape.

Vertical slice architecture

Within a module, a feature is one cohesive slice: command or query, handler, validator, DTO, and mapper together. Adding a feature means adding a slice, not scattering edits across horizontal folders.

Modular monolith, extractable services

Modules implement one contract and are registered in dependency order; each can later run as its own service host behind a YARP gateway without a rewrite, because application code talks to abstractions and transport lives at the edges.

Cross-service auth without shared secrets

Only the Identity service holds token-signing key material: every other service validates its RS256 tokens via JWKS / OIDC discovery, so no symmetric secret ever crosses a service boundary and key rotation is publish-once at the issuer.

Write-once UI, render everywhere

A page is authored once as a Razor component and hosted by both the Blazor web host and the .NET MAUI hybrid host: Web, Android, iOS, macOS, and Windows with no per-platform reimplementation.

Event-driven integration, with an outbox

Domain events are persisted in the same transaction as the data, then delivered at least once by a background processor, in-process for the monolith or over a broker once extracted. No save-then-publish dual-write bug.

Database-per-service

Each module or service owns its own database and its own outbox. Cross-source relationships auto-degrade to batch loaders, and the outbox is the cross-source consistency mechanism.

Engine-agnostic entities

A domain entity carries no persistence-engine choice: a one-token attribute on its configuration routes it to SQL Server, Cosmos DB, or SQLite with zero change to the entity or the application layer. Plumbing shipped and tested.

The Result pattern

Expected error paths return a Result carrying typed errors instead of throwing exceptions: the single most pervasive idiom in the codebase.

Soft-delete, audit, and erasure

Entities are never hard-deleted: global query filters hide them, audit fields are stamped centrally on every save, an opt-in field-level audit trail writes in the same transaction, and a separate anonymize path satisfies GDPR/CCPA erasure.

Multi-tenancy without a fork

An opt-in named tenant filter composes with the soft-delete filter, a write interceptor refuses cross-tenant writes, and a per-tenant connection-string override buys full database isolation, all from the same entity model.

Identifier type aliases

Every entity ID is a per-module type alias rather than a bare int or Guid, linked into every project, so an ID type changes in exactly one place.

Graded honestly

A two-axis architecture scorecard

Every repo is scored against a 34-category rubric on two axes, Maturity (how complete the decision is) and Implementation (how well it is realized in code), with evidence and recorded gaps rather than a single vanity number.

Why, not just what

Architecture Decision Records

111 ADRs capture the context and trade-offs behind each cross-cutting pattern, so the design is teachable, not tribal knowledge. Every entry below links to the full record.

001Manual DTO mappingPer-entity source-generated mappers chosen over reflection-based AutoMapper. 002Navigation populatorsCross-container and cross-source eager loading via an explicit populator contract. 003Outbox dual dispatchOutbox plus in-process dispatch plus a background processor for at-least-once delivery. 004Cross-service token validation (JWKS)Extracted services validate RS256 tokens via JWKS / OIDC discovery, no shared key. 005Soft-delete vs. erasureSoft-delete for lifecycle; anonymization plus outbox purge for GDPR/CCPA erasure. 006Database per serviceEach service owns its DB and outbox; one context class, one instance per database. 007gRPC cross-service callsShared contracts, typed clients, and Result-over-the-wire for synchronous calls. 008Monolith to services plus gatewayOne service host per module behind a YARP gateway; transport at the edge keeps it reversible. 009Resilience and recovery objectivesA standard resilience handler on every outbound client; declared RTO/RPO and drilled restore. 010Integration-event schema versioningEvery event carries a SchemaVersion; breaking changes use a new event type plus upcaster. 011Single-locale by designSuperseded by ADR-027: en-US only was a deliberate, revisitable non-goal. 012gRPC-host transport conventionTwo coherent Kestrel profiles; the choice forces the gateway-forward mode and JWKS routing. 013Result pattern over exceptionsExpected failures are Result values; only the edge maps to HTTP or gRPC status. 014CQRS decorator pipelineThin handlers behind a Scrutor decorator chain whose order is load-bearing. 015Architecture fitness functionsInvariants gate the build twice: a compile-time layer guard plus a shared NetArchTest library. 016Lockstep versioning + MassTransit pinAll packages release at one version; the MassTransit v8 pin is a build gate. 017HTTP request idempotencyAn attribute dedups client retries via an Idempotency-Key header and cached replay. 018Polyglot persistenceSQL Server, Cosmos, and SQLite behind one model; engine is an attribute on the config. 019Layered rate limitingAn always-on global limiter caps authenticated callers per user and exempts infra traffic. 020Permission-based authorizationAn opt-in capability layer over RBAC; permission policies resolve on demand from a registry. 021Consumer-side inbox idempotencyAn opt-in inbox dedups broker redeliveries by message id in the consumer's own database. 022Browser session-cookie authHttpOnly cookies plus an SSR-time non-validating scheme so [Authorize] passes during prerender. 023Security-response headers plus CSPHardened security-headers middleware with a pluggable CSP that cannot break Blazor. 024Two-channel user notificationsOne use case writes a durable inbox and fires a transient SignalR push; transport is pluggable. 025Startup warm-up plus readiness gatingWarm-up tasks run at startup and a readiness gate holds probes off a warming replica. 026Two-tier cachingOne swappable cache substrate (in-memory or Redis) plus an HTTP output-cache edge for public reads. 027Multi-locale i18n (supersedes 011)English and Spanish via resource files; backend errors localized at the edge by error code, one culture cookie across SSR, Server, and WASM. 028Day / dark theme modeA persisted light/dark toggle bound through MudThemeProvider, defaulting to the OS preference with a no-flash cookie bootstrap. 029Brute-force login protectionEmail-keyed login lockout with exponential backoff plus a per-IP registration cap, covering the anonymous surface the rate limiter exempts. 030Startup sole-migratorEach service applies its own EF migrations at boot and is the sole migrator; no deploy-step backstop. 031Feature-flag managementOne flag name enforced on two surfaces: a controller gate and the outermost CQRS decorator; disabled reads as 404. 032Password hashing with legacy migrationPBKDF2 with 600k iterations for new passwords; legacy records still verify and migrate on the owner's next password set. 033Resource-ownership authorizationA row-level ownership axis beside RBAC: an owner-or-admin filter rejects mismatches and a specification row-scopes queries. 034Generic entity controllersEvery entity inherits a REST surface plus a bounded query contract: sparse fields, typed filters, sort, and pagination. 035Optimistic concurrency (RowVersion)A RowVersion token round-trips through DTOs so a stale write surfaces as HTTP 409, gated by a fitness rule. 036External OAuth loginGoogle and GitHub sign-in swap a single-use code for local JWTs; provider tokens never ride the redirect URL. 037Field-level encryption at restAn EF converter encrypts string columns with AES-256-GCM; shipped and tested, not yet wired to an entity. 038Supply-chain provenanceAn SBOM release gate, committed lock files, a transitive vulnerability audit, and package sources pinned to nuget.org. 039Live channel pushEphemeral channel events ride the existing notification hub, so one WebSocket carries durable notifications and lossy live events. 040Authenticated output cachingPublic, user-independent GET endpoints cache even when requests carry a Bearer token; identity-dependent payloads never qualify. 041Observability and telemetryA shared OpenTelemetry baseline plus CQRS duration metrics, correlation IDs, head sampling, and outbox-poll span filtering. 042Device capability abstraction (MAUI)Per-capability contracts with browser fallbacks; the MAUI package overrides them for native heads (the fifteenth package). 043Mobile deep links and OAuth callbackAllow-listed custom-scheme OAuth completion for MAUI plus app-association files served by each app's web host. 044Native push deliveryA third notification channel: OS-level FCM/APNs push via Azure Notification Hubs reaches backgrounded and killed apps; the inbox stays the source of truth. 045Managed file storage and avatarsPluggable blob storage plus an image processor that strips all metadata and re-encodes uploads; avatars land as 256x256 JPEGs in a public-read container. 046HTTP API versioning strategyHeader-based versioning wired in one call; supported and deprecated versions are reported on every response, with a fitness contract asserting the headers. 047Soft-deleted-user session revocationA middleware returns 401 for authenticated callers whose account is soft-deleted, bounding the stateless-JWT revocation window to a 30-second cache instead of the token lifetime. 048Primitive identifier type aliasesEntity IDs stay primitives behind per-module type aliases, chosen over strongly-typed ID structs: readable signatures with zero EF, serializer, or OpenAPI friction. 049Library-scoped ConfigureAwait policyFramework packages await with ConfigureAwait(false), enforced by CA2007 at error severity; application repos keep the analyzer off because ASP.NET Core has no synchronization context. 050JWT + single rotating refresh tokenA short-lived stateless JWT plus one server-stored, opaque refresh token per user that rotates on every use; each rotation slides a fixed inactivity window, and a mismatch revokes the chain. 051Client-side auth token lifecycle across render modesOne ITokenRefresher abstraction with head-specific strategies: browser heads refresh through the same-origin proxy cookie, MAUI refreshes directly and persists the rotated pair in OS SecureStorage. 052Background job executionWork that outlives a request runs as a bounded channel plus a single-reader hosted drain, never an untracked task, so the host can cancel and await it on shutdown. 053Dual-registry package publishingEvery release pushes the same packages to nuget.org and GitHub Packages from one tag, authenticated by a short-lived OIDC exchange rather than a stored key; nuget.org is the documented install path because the GitHub registry needs a token even for public packages. 054Saga compensation + reconciliation backstopCross-boundary consistency without two-phase commit: each step raises a domain event and its compensating action runs in its own scope, made idempotent by a marker committed alongside the compensating writes, with a periodic sweep as the saga-timeout backstop. 055Repository + Specification data-access contractThe read contract splits into id lookups and collection queries, with the raw IQueryable surfaces confined to the composite, because a raw-queryable handler is EF-coupled and cannot move behind a gRPC boundary later. 056Blazor render-mode strategyInteractiveAuto is declared once on the root router with prerendering left on, and the resulting double fetch is cancelled once in the shared list-page base by persisting the prerendered payload rather than by turning prerendering off. 057Expand/contract schema evolution as a CI gateA migration added by a PR cannot drop a column, table, or index without an explicit override marker, because deploy rollback is revision-only and never reverts schema: the previous release has to keep running against the new one. 058Runtime conformance suites shipped as a packageAbstract contract bases that a host subclasses to prove it wired the framework's runtime contracts, asserted against a really booted host rather than by reflection over registrations. The runtime half of the fitness-function story. 059The IModule contract and module compositionModules are discovered by reflection and registered in topological dependency order, and a disabled module is represented by stub registrations rather than by absence: the mechanism that lets one module run as its own service. 060Performance-regression gateA committed benchmark baseline checked in CI, gating allocations absolutely but latency only as a ratio between benchmarks, which is what keeps the gate meaningful on shared, noisy runners. A missing measurement fails rather than passes. 061Runtime secret management via Key Vault referencesEvery production secret lives in Azure Key Vault and reaches the app as a reference resolved by one shared user-assigned managed identity: no container app carries an inline value, the same identity pulls from the registry, and SQL authentication is staged behind a flag toward the same model. 062SLO alerting as code with an alert-to-runbook gateEach consumer declares its SLO alerts as data in its Bicep template, materialized as Log Analytics scheduled query rules whose predicates exclude the routine 401s and hub-connection lifetimes that used to page, with a framework build gate failing when an alert and its runbook section fall out of step. 063WCAG 2.1 AA as a shipped test contractAccessibility conformance as a named, versioned contract in the E2E package: one pinned axe tag list with advisory rules deliberately outside it, a violation thrown as a test failure pointing at the element, inherited by consumers through the workflow bases and wired as a merge check and deploy gate. 064Deploy preconditions as proof-of-recency gatesA production deploy is blocked not only on green tests but on proof that the out-of-band verification still means something: three gates assert a real DR drill, load run, and broker round-trip happened recently, because a scheduled workflow can fail silently for weeks behind a green-looking history. 065Scaffolding templates derived from the reference appA dotnet new template pack (mmca-app, mmca-module, mmca-command, mmca-query) generated from the minimal seed, replacing the 12 projects, 105 files, and 6,596 hand-created lines that stood between the getting-started guide and the first line of business logic. 066Broker transport selection and dev/prod parityOne IMessageBus abstraction with a three-value transport selector chosen at the deployment edge, never in application code: the AppHost wires RabbitMQ locally, Bicep injects Azure Service Bus in production, both configured identically, and a dedicated test tier exercises the transport only production uses. 067Shared Blazor shell + IUIModule compositionThe application shell (router, layout, nav, and the auth and notification pages every app needs) ships in the framework package, and each module contributes pages and navigation by implementing IUIModule, resolved from DI so nothing in the shell names a module: ADR-059's composition model applied to the UI layer. 068Value objects as validated domain primitivesA domain value that carries an invariant is an immutable record value object with a private constructor and a Result-returning factory, while identifiers stay primitives (ADR-048): the asymmetry is the decision, and the factory shape is fitness-enforced rather than conventional. 069Shared DataProtection key ring for scaled-out hostsOne opt-in registration persists the DataProtection key ring to a single Azure blob so every replica of a scaled-out host can decrypt the cookies and antiforgery tokens its peers mint; an absent configuration key means the single-process in-memory default stays, with no Azure dependency at startup. 070Fail-fast configuration contractEvery settings section binds through data-annotation validation with ValidateOnStart, so a missing or unparseable value refuses to boot the host instead of surfacing as a 500 at first use, and a setting read above Infrastructure goes through a read-only interface rather than the options pipeline. 071Barcode scanning and QR display capabilityOne feature split by what each half actually depends on: rendering a QR needs no device, so it ships as a plain shared component, while reading one needs a camera and ships as a capability contract with a null fallback and an opt-in native implementation, because a camera costs an app-store permission declaration a head may not want. 072QR badge check-in and points gamificationThe badge QR carries an opaque server-verified handle rather than a signed token, organizers scan attendees rather than the reverse, and one unique index serves as both the redelivery-idempotency guard and the anti-farming rule for an append-only points ledger. 073Multi-tenancy: shared schema plus DB-per-tenantA second named query filter composes by AND with the existing soft-delete filter and reads the tenant live from the executing context, so one cached model per source serves every tenant with the tenant id as a parameter; a write interceptor refuses cross-tenant writes, and a per-tenant connection-string override buys database isolation without a second model. 074Recurring job schedulerDurable cron jobs built on the outbox's own claim-lease idiom rather than on Hangfire or Quartz, because the framework already owns a lease-based, multi-replica-safe polling loop in production; a missed schedule runs once and then advances, so an outage cannot produce a catch-up storm. 075Audit trail: field-level change historyA fourth save-changes interceptor diffs the change tracker and writes per-property history rows in the same transaction as the data, opted in per entity by a marker; personal data is redacted at capture rather than at read, because a trail outlives the row it describes. 076Data-subject export (DSAR) contractThe orchestration every export shares (ownership gate, aggregate load, section fan-out, envelope) moves into the framework while the app-specific projections stay app-specific as registered sections; a failing section degrades to an unavailable flag rather than failing the package, because a subject request is a legal deadline. 077HybridCache as an opt-in cache substrateAn L1-plus-L2 implementation joins the two auto-selected cache stores, opted into explicitly, and writes under a disjoint keyspace so two serialization formats can never share one key: the structural fix for the class of bug that once produced a production WRONGTYPE, including mid-rolling-deploy. 078CSV export as a dedicated endpointExport ships as its own route rather than as content negotiation, because the output-cache policy does not vary by Accept and a negotiation failure silently returns JSON; the endpoint page-loops past the pipeline's unpaged row ceiling and streams RFC 4180 rows written in-house, with truncation signalled by a header. 079Shared HTTP middleware pipelineOne framework method fixes the middleware order for every REST/gRPC host, with the load-bearing adjacencies (authentication before the rate limiter and before tenant resolution) commented where they sit; hosts extend by appending after the call, and conditional middleware registers unconditionally but stays inert by config. 080Rollout with automatic revision rollbackEvery production deploy ends in a smoke gate asserting expected status codes; on failure each container app walks back to its previous revision, and a rollback that itself fails escalates loudly as a fleet split. Schema is never rolled back, which is the premise the expand/contract gate rests on. 081Cost baseline as a deploy gateA read-only workflow asserts the production footprint still matches its declared cost baseline (replica ceilings, SQL tiers) and sits in deploy.needs, so an un-reverted manual surge blocks the next deploy instead of quietly billing forever. 082Two-tier cross-origin postureService hosts run named allow-listed CORS policies selected per environment inside the shared pipeline; the gateways run a default policy that restricts only origins, because a reverse proxy has to pass arbitrary client headers through. 083CRUD lifecycle event taxonomyOne entity-changed event with a state discriminator replaces per-entity Created/Updated/Deleted triples, and handlers filter on the state; business state-machine transitions keep their own event types, and the discriminator rides integration events as a frozen wire field. 084Stripe webhook ingress contractAn anonymous signature-verified endpoint whose status code means accepted-not-processed, because rejections make the provider disable the endpoint; a startup service self-registers the webhook and logs its freshly minted signing secret loudly for an operator to persist. 085Identifier Type Aliases Revisited (Wrapper Structs Deferred Again, With Triggers)Accepted (2026-08-18). Revised 2026-08-23 (the alias count and the migration-surface census were recounted, the census gained a stated methodology, and… 086A Process Manager Is Deferred, Not Absent (Relates to ADR-054)Accepted (2026-08-18) as a documented deferral. Nothing ships with this record: no state machine, no correlation store, no new package. What ships is… 087Broker Poison-Message Handling: Second-Level Redelivery and Fault ObservabilityAccepted (2026-08-18). Amends ADR-009: the outbox's broker publish gains a circuit breaker, which is the first resilience policy this workspace applies… 088Gateway Edge Responsibilities (and the Three It Declines)Accepted (2026-08-18). Extends ADR-019 with a fourth, edge-tier layer whose posture is the deliberate opposite of the service tier's authenticated-only… 089Gateway Topology Owned by ConfigurationAccepted (2026-08-18; revised 2026-08-23: ADC's table gained a 27th route, /Activities, on 2026-08-19, and the bicep anchors below are corrected;… 090Event Upcaster Registration Extension PointAccepted (2026-08-21). Completes the follow-up named in ADR-010: that record established the versioning policy (a SchemaVersion signal plus a… 091Cache-Backed Password ResetAccepted (2026-08-22). Extends ADR-029 (the cache-backed login-protection idiom this record reuses) and ADR-032 (which decided how a password is… 092Core Web Vitals Budget as a Shipped Test Contract and Deploy GateAccepted (2026-08-23). Revised 2026-09-01: the measurement flow itself now ships as the one-call IPage.MeasureWebVitalsAsync extension that every suite… 093Container Image Build and Runtime PostureAccepted (2026-08-23) for the three build decisions below. The two runtime postures in "Open postures" are recorded as undecided: they describe what… 094Client-Side Entity Data-Access ContractAccepted (2026-08-23). Revised 2026-08-31: GetByIdAsync is recorded with its real signature (id, includeChildren, CancellationToken; a missing entity… 095Uniqueness Under Soft Delete (Filtered Unique Indexes)Accepted (2026-08-23). Revised 2026-08-26 (the convention appends its clause to a hand-authored filter instead of skipping the index). 096Best-Effort Side-Effect ContractAccepted (2026-08-23). Revised 2026-08-31. 097Multi-Device Refresh Sessions (Hashed, Rotating, Per Device)Accepted (2026-08-26). Supersedes the storage model of ADR-050 (one plaintext refresh-token column on the user row); the rotation and reuse-detection… 098Aspire for Orchestration, Not for Testing or Production DashboardsAccepted (2026-08-28). Revised 2026-08-31: the sanctioned nightly AppHost smoke test is implemented, so Aspire.Hosting.Testing now ships in exactly one… 099Generic Write-Side Entity Commands (Create, Update, Delete Without a Handler per Aggregate)Accepted (2026-08-29, framework v1.170.0). Extends ADR-034, which gave every entity a generic read surface plus create and delete, by completing the… 100The Outbox Is Resolved from the Messaging Mode, Not Always OnAccepted (2026-08-29, framework v1.170.0). Amends ADR-003 (the outbox runs when the transport needs it, rather than in every host unconditionally) and… 101An MMCA.Common Metapackage for the Core SixAccepted (2026-08-29, framework v1.170.0). Extends ADR-053 (the metapackage ships to both registries from the same tag, through the same workflow) and… 102PBKDF2-Only Password HashingAccepted (2026-08-31). Supersedes ADR-032. 103bUnit Component-Test Tier as a Shipped PackageAccepted (2026-08-31). Revised 2026-09-03: one trade-off overstated how far the AngleSharp advisory pin travels. See Revision (2026-09-03) at the end. 104Plain Enums by Default, Enumeration<T> as the Opt-In Smart-Enum BaseAccepted (2026-08-31). 105The Published Data-Residency Claim as a Build GateAccepted (2026-09-01). 106C# Extension Members as the Public DI Registration SurfaceAccepted (2026-09-01). 107Transaction Execution and Commit-Ambiguity ContractAccepted (2026-09-03). 108Cross-Replica Mutual Exclusion via IDistributedLockAccepted (2026-09-03). 109Feature-by-Folder Layout as an Enforced ConventionAccepted (2026-09-03). 110Rubric Version 2, Category Realignment at 34Accepted (2026-09-04; section 16 scope corrected 2026-09-04: MMCA.ADC scores the category, recorded in ADR-111). 111AI Session Scoring GovernanceAccepted (2026-09-04).

Read the source of truth

The reference library

The full architecture documentation, maintained in this site's repository as its canonical home and rendered as browsable pages: every Architecture Decision Record, the governance scorecards, the guides and specifications, and the complete onboarding guide that walks the codebase type by type.

Start here

Use it, read it, or follow along

The framework is Apache 2.0 and published to nuget.org. The fastest way in is the reference app: one module wired end to end through all five layers, with the extraction path already in place. Commercial support (assessments, workshops, embedded advisory) is available through Cleavestack.

Try the reference app

MMCA.Helpdesk is a runnable seed built against the framework, and the worked companion to the getting-started guide. Every step in the guide maps to real code in that repo.

Install the packages

Every package releases in lockstep at one version. The MMCA.Common metapackage is one reference that installs the six core packages; add Grpc, UI, Gateway, or Testing.* as you need them.

Read the code

The framework and the reference app are public. The two production applications that track it are the source of the case-study material in the docs.