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

Writing

Deep dives on enterprise .NET

A long-form series that turns the MMCA framework's architecture decisions into teachable patterns, every claim grounded in real source. Read in order it is a curriculum: the first few orient you, the middle ones cover the core patterns, data, and the API edge, and the last group is proof and getting started.

The series is rolling out on Medium. Titles and summaries are below; each "Read on Medium" link activates as that piece publishes.

All articles

Orientation · No. 1

Open-sourced and graded against 34 categories

Why I open-sourced a production .NET framework and scored it against a 34-category architecture rubric, gaps and all.

Orientation · No. 2

Modular monolith to microservices

The cornerstone idea: build the monolith now and extract a service later with no rewrite, via module discovery, gRPC contracts, and a YARP gateway.

Orientation · No. 3

The 34-category architecture rubric

A two-axis rubric for scoring architecture on maturity and implementation, so 'good architecture' stops being a vibe.

Core patterns · No. 4

The Result railway in C#

Model expected failures as Result values with a transport-agnostic error type, and keep exceptions for the genuinely exceptional.

Core patterns · No. 5

Kill the anemic domain model

Push behavior into rich aggregates with factory methods and invariants instead of bags of public setters.

Core patterns · No. 6

Specifications over LINQ spaghetti

Compose queries from reusable specification objects instead of scattering LINQ across handlers.

Core patterns · No. 7

The CQRS decorator pipeline

Thin command and query handlers wrapped by a Scrutor decorator chain whose order is load-bearing.

Core patterns · No. 8

Compose validators, don't copy them

A validation kit that composes FluentValidation rules instead of copy-pasting them across features.

Core patterns · No. 9

The transactional outbox

Events that survive a crash: persist them atomically with your data, then dispatch at least once.

Data & persistence · No. 10

Database-per-service inside a monolith

Give each module its own database and outbox before you extract it, so extraction changes hosting, not data.

Data & persistence · No. 11

Polyglot persistence: one model, three engines

SQL Server, Cosmos, and SQLite behind a single entity model, with the engine chosen by attribute.

Data & persistence · No. 12

Navigation populators

Eager-load relationships that cross containers and data sources without N+1 or a leaky abstraction.

Data & persistence · No. 13

Optimistic concurrency: RowVersion round-trips

Carry the RowVersion from database to DTO and back, so a concurrent edit fails fast as a conflict instead of silently overwriting.

Data & persistence · No. 14

Self-ordering modules

Modules declare their dependencies and load in topological order, so registration is never hand-sequenced.

Data & persistence · No. 15

Event-schema versioning

Every integration event carries a schema version; breaking changes get a new event type and an upcaster, never a silent reshape.

Auth & the edge · No. 16

JWKS cross-service auth

Validate another service's RS256 tokens via JWKS discovery, with no shared secret crossing a boundary.

Auth & the edge · No. 17

Password hashing done right

The non-negotiables of password storage in .NET, done correctly and tested.

Auth & the edge · No. 18

Idempotency in one attribute

Dedup client retries with an Idempotency-Key header and cached replay, plus a consumer-side inbox for brokers.

Auth & the edge · No. 19

The self-invalidating cache

A caching decorator where commands invalidate and queries populate, plus an authenticated output-cache tier at the API edge.

Auth & the edge · No. 20

Problem Details across HTTP and gRPC

One error contract mapped consistently to HTTP Problem Details and gRPC status.

Auth & the edge · No. 21

Notifications as a vertical slice

A notifications feature built as a clean vertical slice across every layer.

Auth & the edge · No. 22

Live channels over one SignalR hub

Sub-second ephemeral events (polls, Q&A, live counts) fanned out over the existing notification hub, with nothing persisted.

Auth & the edge · No. 23

Delete AutoMapper: manual DTO mapping

Why source-generated, per-entity mappers beat reflection-based mapping for clarity and speed.

Auth & the edge · No. 24

Permission-based authorization over roles

A capability layer over RBAC: permission policies that resolve on demand from a central registry.

Auth & the edge · No. 25

Browser session-cookie auth for Blazor SSR

HttpOnly session cookies and an SSR-time scheme so [Authorize] passes during prerender, with the API still the boundary.

Auth & the edge · No. 26

External OAuth login behind your own JWTs

Sign in with Google or GitHub without leaking provider tokens: external identity exchanged for your own JWTs at the boundary.

Auth & the edge · No. 27

One rotating refresh token

A short-lived JWT plus one server-stored refresh token that rotates on every use, with reuse detection that makes a stolen token end its own session.

Auth & the edge · No. 28

Generic entity controllers

A write-once REST surface every entity inherits, plus a bounded dynamic query contract that is never open SQL.

Auth & the edge · No. 29

Resource-ownership authorization

Beyond roles and permissions: which rows you may touch, enforced per resource.

Auth & the edge · No. 30

Rate limiting and brute-force protection

Two layers that cover the whole API edge: endpoint rate limits plus lockout-based brute-force defense on identity.

Run & extract · No. 31

Aspire: one command

Model services, databases, and the broker as one Aspire graph that runs from laptop to Azure with one command.

Run & extract · No. 32

Extracting a module to a gRPC service

A step-by-step extraction of an in-process module into its own gRPC service, database, and auth.

Run & extract · No. 33

Resilience and recovery objectives

Standard resilience on every outbound client, plus declared RTO/RPO and a drilled restore.

Proof & getting started · No. 34

Architecture fitness functions

Architecture rules that fail the build: a compile-time layer guard plus a shared NetArchTest rule library.

Proof & getting started · No. 35

The test pyramid

How the framework's tests stack up: fast unit and architecture tests at the base, E2E at the tip.

Proof & getting started · No. 36

Soft-delete vs the right to erasure

Soft-delete for lifecycle, anonymization plus outbox purge for GDPR/CCPA erasure, and why both exist.

Proof & getting started · No. 37

A reusable Blazor UI framework

A shared Blazor and MudBlazor UI layer with accessibility enforced by axe in CI.

Proof & getting started · No. 38

i18n and theming on one preference pipeline

A culture choice and a theme choice ride the same cookie, profile column, and login reconciliation: one persistence path, two switches.

Proof & getting started · No. 39

Build your first module

A hands-on walkthrough of building a new module across all five layers.

Proof & getting started · No. 40

Write your first fitness test

Author your first architecture fitness test and watch it fail the build on a violation.

Proof & getting started · No. 41

Two real apps on one framework

A case study: a conference platform and an e-commerce store built on the same kernel.

Proof & getting started · No. 42

One Blazor UI, two hosts

The same Blazor components run in a browser and inside a MAUI hybrid app; small per-capability contracts reach native hardware without ever asking 'am I on mobile?'.

Data & persistence · No. 43

Managed file storage: uploads you don't have to trust

Attacker-controlled bytes become safe avatars: content sniffing, metadata stripping, re-encoding, and pluggable blob storage.

Auth & the edge · No. 44

HTTP API versioning, proven not just claimed

Header-based versioning introduced without breaking a single caller, plus a fitness contract that proves two live versions coexist.

Core patterns · No. 45

Feature flags in the CQRS pipeline

Gate commands and queries at the outermost decorator, so a handler never checks a flag and a disabled feature is rejected before any work runs.

Data & persistence · No. 46

Field-level encryption in EF Core

An AES-256-GCM value converter that keeps a PII column ciphertext even for someone who can query the database.

Auth & the edge · No. 47

Security headers and CSP for Blazor

One middleware stamps hardened response headers on every host, with the Blazor CSP resolved through a pluggable provider.

Run & extract · No. 48

Observability by default

A shared OpenTelemetry baseline with CQRS duration metrics, correlation IDs, and outbox-poll span filtering, exported to Azure Monitor.

Core patterns · No. 49

Saga compensation and the reconciliation backstop

Undo as a first-class event handler: give back stock a committed transaction already took, with a periodic sweep as the saga-timeout backstop.

Proof & getting started · No. 50

The series index

The full series index and recommended reading order.