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

Onboarding guide

MMCA Concept & Pattern Maps

Mermaid diagrams distilled from the Onboarding guide (primer, group taxonomy, dependency manifest, and the 27 group chapters). Each diagram captures a relationship between the concepts the guide teaches: the layering, the 27 functional groups, the cross-cutting patterns, and the ADRs (see Website/docs-src/adr/README.md for the canonical range) / rubric categories that explain the "why".

Diagrams are grounded in: 00-primer.md · 00-group-taxonomy.md · 00-dependency-manifest.md · the group-NN-*.md chapters.


1. System context, two codebases + the 17 packages

MMCA.Common is a framework published as seventeen NuGet packages in lockstep, to nuget.org and GitHub Packages from one tag (ADR-053); MMCA.ADC and MMCA.Store consume them. MMCA.Common/FACTS.md owns the count and the list (link there, do not recount). The framework depends on neither consumer (that one-way arrow is why the Common groups come first in the guide). Two packages are not layers: MMCA.Common itself is a metapackage that ships no assembly and bundles the six a standard host always takes (Shared, Domain, Application, Infrastructure, API, Aspire) (ADR-101), and MMCA.Common.Gateway is the YARP edge kit that sits beside the Aspire host kit and takes no first-party project reference at all (ADR-088). UI.Maui is the one MAUI-TFM package: it lives outside MMCA.Common.slnx and is built and packed by dedicated windows CI jobs (ADR-042).

flowchart TD
    subgraph COMMON["MMCA.Common: framework, 17 NuGet packages (lockstep versioned)"]
        direction TB
        subgraph CORE["Core (4)"]
            SH["Shared"]
            DOM["Domain"]
            APP["Application"]
            INF["Infrastructure"]
        end
        subgraph PRES["Presentation / transport (5)"]
            API["API"]
            GRPC["Grpc"]
            UI["UI"]
            UIW["UI.Web"]
            UIM["UI.Maui<br/>(outside the slnx)"]
        end
        subgraph HOSTPK["Hosting / edge (3)"]
            ASPIRE["Aspire"]
            ASPH["Aspire.Hosting"]
            GWPK["Gateway<br/>(YARP edge kit, ADR-088)"]
        end
        subgraph TEST["Testing (4)"]
            T1["Testing"]
            T2["Testing.E2E"]
            T3["Testing.UI"]
            T4["Testing.Architecture"]
        end
        META["MMCA.Common<br/>(metapackage, no assembly:<br/>the Core 6, ADR-101)"]
    end

    ADC["MMCA.ADC: Atlanta Developers Conference app<br/>(Conference · Engagement · Identity · Notification)"]
    STORE["MMCA.Store: e-commerce app<br/>(out of scope in the guide)"]

    COMMON -->|"consumed by"| ADC
    COMMON -->|"consumed by"| STORE

    classDef fw fill:#e8f0fe,stroke:#4285f4,color:#111
    classDef con fill:#e6f4ea,stroke:#34a853,color:#111
    class SH,DOM,APP,INF,API,GRPC,UI,UIW,UIM,ASPIRE,ASPH,GWPK,T1,T2,T3,T4,META fw
    class ADC,STORE con

2. Clean Architecture, the layered dependency rule

Source dependencies point inward toward the Domain; each layer references only layers below it. Deliberate exceptions: UI and Grpc depend on Shared only (UI for Blazor WASM compatibility, Grpc because it is pure transport, and Aspire in fact references only Shared too); UI.Maui references UI (and Shared through it) only; and UI.Web, the Blazor Web host bridge, sits above UI, API and Aspire, with transitive project references disabled so the guard can tell a deliberate direct reference from an inherited one. Gateway sits outside the rule set entirely: it carries no guard target and no first-party project reference at all (YARP only). Enforced twice: the compile-time MSBuild layer guard in Source/Build/MMCA.Common.LayerEnforcement.targets, which carries a target per guarded project (Shared, Domain, Application, Infrastructure, UI, UI.Maui, UI.Web) and fails the build on a forbidden ProjectReference, and NetArchTest fitness tests that re-assert the same rules against compiled assemblies (ADR-015).

flowchart TD
    APIL["API<br/><i>controllers, middleware, filters, startup</i>"]
    INFL["Infrastructure<br/><i>EF Core, caching, JWT/JWKS, outbox, message bus, SignalR</i>"]
    APPL["Application<br/><i>CQRS handlers, decorators, module system, IMessageBus (ports)</i>"]
    DOML["Domain<br/><i>entities, aggregates, domain events, specifications</i>"]
    SHRL["Shared<br/><i>Result pattern, errors, DTOs, value objects</i>"]
    UIL["UI (Blazor / MudBlazor)"]
    GRPCL["Grpc (pure transport)"]
    ASPL["Aspire (service defaults)"]
    UIMA["UI.Maui (MAUI heads, ADR-042)"]
    UIWB["UI.Web (Blazor Web host bridge)"]
    GWL["Gateway (YARP edge kit:<br/>no first-party reference, ADR-088)"]

    APIL --> INFL --> APPL --> DOML --> SHRL
    UIL -.->|"Shared only"| SHRL
    GRPCL -.->|"Shared only"| SHRL
    ASPL -.->|"Shared only"| SHRL
    UIMA -->|"UI + Shared only"| UIL
    UIWB --> UIL
    UIWB --> APIL
    UIWB --> ASPL

    ENF["Enforced 2x: compile-time layer guard + NetArchTest fitness (ADR-015)"]
    ENF -.-> APIL

    classDef layer fill:#fef7e0,stroke:#f9ab00,color:#111
    classDef exc fill:#fce8e6,stroke:#ea4335,color:#111
    classDef note fill:#f1f3f4,stroke:#9aa0a6,color:#333
    class APIL,INFL,APPL,DOML,SHRL layer
    class UIL,GRPCL,ASPL,UIMA,UIWB,GWL exc
    class ENF note

3. The 27 functional groups, dependency / build order

The primary axis of the guide: every type lives in exactly one of 27 chapter groups, ordered roughly topologically. Foundational, widely-depended-on concerns first (Result → domain blocks → querying → events → CQRS → …), then the ASP.NET/UI/Aspire edges, then the ADC business modules, then the late-added Common device-capability layer and the test infrastructure. Arrows show the dominant "builds on" direction (charters + levels).

flowchart TD
    G01["G01 Result &amp; Error"]
    G02["G02 Domain Building Blocks"]
    G03["G03 Querying / Specifications"]
    G04["G04 Events + Outbox"]
    G05["G05 CQRS Pipeline"]
    G06["G06 Validation"]
    G07["G07 Persistence / EF Core"]
    G08["G08 Auth &amp; Authorization"]
    G09["G09 Caching"]
    G10["G10 Notifications"]
    G11["G11 Navigation Populators"]
    G12["G12 API Hosting / Mapping"]
    G13["G13 gRPC Contracts"]
    G14["G14 Module System / Composition"]
    G15["G15 Common UI Framework"]
    G16["G16 Aspire Orchestration"]

    subgraph ADCMOD["MMCA.ADC business modules (bounded contexts)"]
        direction TB
        G17["G17 Conference · Domain"]
        G18["G18 Conference · Application"]
        G19["G19 Conference · Infrastructure"]
        G20["G20 Conference · API / gRPC"]
        G21["G21 Conference · UI"]
        G22["G22 Engagement · bookmarks, check-in, points"]
        G23["G23 Engagement · live layer (polls, Q&amp;A)"]
        G24["G24 Identity module"]
        G25["G25 ADC Host / Shell / Composition"]
    end

    G26["G26 Device Capability Layer<br/>(MMCA.Common, appended after the modules)"]
    G27["G27 Testing &amp; Quality Infrastructure"]

    %% framework backbone
    G01 --> G02 --> G03
    G01 --> G06
    G02 --> G04
    G01 --> G05
    G04 --> G05
    G06 --> G05
    G09 --> G05
    G03 --> G07
    G04 --> G07
    G02 --> G08
    G05 --> G12
    G06 --> G12
    G08 --> G12
    G03 --> G11
    G07 --> G11
    G04 --> G10
    G07 --> G10
    G01 --> G13
    G14 --> G07
    G12 --> G15
    G08 --> G15
    G15 --> G26

    %% edges into composition + orchestration
    G05 --> G14
    G07 --> G14
    G10 --> G14
    G14 --> G16
    G13 --> G16

    %% ADC builds on the framework
    G02 --> G17
    G17 --> G18
    G05 --> G18
    G18 --> G19
    G07 --> G19
    G18 --> G20
    G13 --> G20
    G18 --> G21
    G15 --> G21
    G05 --> G22
    G22 --> G23
    G10 --> G23
    G13 --> G23
    G08 --> G24
    G14 --> G25
    G16 --> G25
    G20 --> G25
    G21 --> G25
    G26 --> G25

    %% everything is tested
    ADCMOD --> G27
    G16 --> G27
    G26 --> G27

    classDef fw fill:#e8f0fe,stroke:#4285f4,color:#111
    classDef adc fill:#e6f4ea,stroke:#34a853,color:#111
    classDef test fill:#f3e8fd,stroke:#a142f4,color:#111
    class G01,G02,G03,G04,G05,G06,G07,G08,G09,G10,G11,G12,G13,G14,G15,G16,G26 fw
    class G17,G18,G19,G20,G21,G22,G23,G24,G25 adc
    class G27 test

4. Core framework patterns, how the building blocks compose

The pattern-level view of the same backbone: the ideas the primer commits to and how they feed each other. Result is the pervasive currency; DDD blocks produce domain events; events feed the outbox; commands/queries flow through the decorator pipeline; persistence writes both entity and outbox in one transaction.

flowchart LR
    RESULT["Result / Error<br/>(railway, ADR-013)"]

    subgraph DDD["Domain-Driven Design (G02, G17)"]
        AGG["Aggregate root + child entities"]
        VO["Value objects + invariants (ADR-068)"]
        FACT["Factory methods → Result&lt;T&gt;"]
        DE["Domain events"]
    end

    SPEC["Specifications +<br/>dynamic query pipeline (G03)"]
    VALID["FluentValidation<br/>contracts (G06)"]

    subgraph CQRS["CQRS (G05, ADR-014)"]
        CMD["Commands (mutate)"]
        QRY["Queries (read)"]
        PIPE["Decorator pipeline"]
    end

    subgraph EVT["Events + Outbox (G04, ADR-003/100)"]
        OUTBOX["Transactional outbox<br/>(on unless the transport is in-process)"]
        BUS["IMessageBus<br/>(in-process / broker)"]
        INBOX["Consumer inbox (ADR-021)"]
    end

    PERSIST["Persistence / EF Core<br/>SQLServerDbContext (G07)"]
    CACHE["ICacheService (G09, ADR-026)"]

    FACT --> RESULT
    AGG --> DE
    VO --> AGG
    CMD --> RESULT
    QRY --> RESULT
    VALID --> CMD
    CACHE --> QRY
    CMD --> PIPE
    QRY --> PIPE
    PIPE --> PERSIST
    SPEC --> QRY
    SPEC --> PERSIST
    DE --> OUTBOX
    PERSIST -->|"same transaction"| OUTBOX
    OUTBOX --> BUS
    BUS --> INBOX

    classDef core fill:#e8f0fe,stroke:#4285f4,color:#111
    class RESULT,SPEC,VALID,PERSIST,CACHE core

5. Request lifecycle, the CQRS decorator pipeline (ADR-014)

Handlers are thin (one method); every cross-cutting concern is a decorator wrapping the next. Scrutor TryDecorate composes them in reverse registration order (last registered = outermost), so the registration list in AddApplicationDecorators() reads inside-out while the execution order is load-bearing: FeatureGate → Authorization → Logging → Caching → Validating → Timeout → Transactional → Handler for commands, and the same chain minus Transactional for queries (FeatureGate → Authorization → Logging → Caching → Validating → Timeout → Handler). A query is validated too, and deliberately inside caching: a cached entry can only exist because the same query already passed validation when that entry was produced. Authorization (keyed on IRequiresPermission, resolved through IPermissionRegistry) sits outside caching deliberately, so a denied request neither reads nor populates the cache; Timeout (keyed on IHasTimeout) links a per-request budget token to the caller's. Opt-in marker interfaces let a handler switch each concern on, and the shipped DecoratorPipelineOrderTestsBase pins the order rather than comments alone.

flowchart TD
    HTTP["HTTP / gRPC request"]
    EDGE["Edge: controller base / model binder<br/>maps request → command/query (G12)"]
    IDEMP["Idempotent action filter: dedup client retries<br/>(ADR-017, 24h replay, IDistributedLock guard)"]

    subgraph PIPELINE["Decorator pipeline (execution order, outermost→innermost)"]
        direction TB
        FG["FeatureGate: 404 if flag off (ADR-031)"]
        AZ["Authorization: IRequiresPermission via IPermissionRegistry,<br/>outside the cache, denied = Forbidden (ADR-020)"]
        LOG["Logging + RED metrics"]
        CA["Caching: query results in, command invalidation<br/>after commit (ADR-026)"]
        VA["Validating: FluentValidation (G06, commands and queries)"]
        TO["Timeout: IHasTimeout execution budget"]
        TX["Transactional: SaveChanges + outbox (commands only)"]
        H["Concrete handler (one job)"]
    end

    DOMAIN["Domain: aggregate factory / behavior → Result&lt;T&gt;"]
    RESP["Result&lt;T&gt; → edge maps to HTTP/gRPC status (ADR-013)"]
    EXC["Exceptional path: ordered IExceptionHandler chain<br/>OperationCanceled, Domain, DbUpdate, Validation, Global<br/>→ RFC 9457 ProblemDetails (ADR-013)"]

    HTTP --> IDEMP --> EDGE --> FG --> AZ --> LOG --> CA --> VA --> TO --> TX --> H --> DOMAIN
    DOMAIN --> RESP
    H -.->|"thrown, not returned"| EXC

    classDef dec fill:#fef7e0,stroke:#f9ab00,color:#111
    class FG,AZ,LOG,CA,VA,TO,TX,H dec

6. Event-driven integration, outbox dual-dispatch (ADR-003 / 010 / 021 / 100)

Domain events are captured into an OutboxMessage row in the same transaction as the data (no dual-write bug). The two event kinds then part ways: local domain events are dispatched in-process by the interceptor right after the save (deferred until after commit inside a Transactional command) and their outbox rows marked processed, while integration events are never dispatched locally: their rows stay unprocessed until the background OutboxProcessor publishes them through IMessageBus, so the registered transport (in-process for a monolith, a MassTransit broker for extracted services) decides delivery. The processor is also the safety net for a local dispatch that failed. A handler that publishes an integration event itself calls IEventBus.PublishAsync, which writes outbox rows rather than sending inline (the removed IIntegrationEventPublisher is not a caller-facing option any more). Every integration event carries a SchemaVersion; consumers dedup by MessageId via the opt-in inbox. The outbox itself is not unconditional: MessageBus:EnableOutbox is a bool? that resolves an unset value as "the provider is not in-process", so a single-process host dispatches every event inside the process that raised it and runs neither OutboxProcessor nor OutboxCleanupService, while a broker transport with the outbox explicitly disabled throws at registration (ADR-100). The EF model is the same either way, so flipping the flag is a restart and never a migration.

flowchart TD
    AGG["Aggregate raises IDomainEvent / IIntegrationEvent"]
    SAVE["SaveChangesAsync: DomainEventSaveChangesInterceptor"]
    EBUS["Second entry point: IEventBus.PublishAsync from a handler<br/>InProcessEventBus writes the outbox then dispatches;<br/>BrokerEventBus writes the outbox and only signals the processor"]
    subgraph TXN["One DB transaction (per-service DB, ADR-006)"]
        ENTITY[("Entity rows")]
        OBX[("OutboxMessage rows")]
    end
    DISP["IDomainEventDispatcher: in-process handlers<br/>(deferred until after commit)"]
    PROC["OutboxProcessor: background, smart-wait poll,<br/>leased rows for replica scale-out,<br/>opt-in per-key ordered delivery via IHasOrderingKey,<br/>broker publish behind a circuit breaker (ADR-087),<br/>dead-letter on retry exhaustion, with list/replay<br/>through IOutboxAdministration"]
    BUS["IMessageBus: InProcessMessageBus or BrokerMessageBus<br/>→ MassTransit v8 (RabbitMQ / Azure Service Bus)"]
    CONSUMER["IntegrationEventConsumer in another module/service"]
    INBOX[("Opt-in inbox: dedup by MessageId (ADR-021)")]
    HANDLER["IIntegrationEventHandler"]

    AGG --> SAVE
    SAVE --> ENTITY
    SAVE -->|"same commit"| OBX
    SAVE -->|"local domain events"| DISP
    EBUS --> OBX
    OBX -->|"unprocessed rows"| PROC
    PROC -->|"integration events"| BUS
    PROC -.->|"safety net for a failed local dispatch"| DISP
    BUS --> CONSUMER --> INBOX --> HANDLER
    SCHEMA["SchemaVersion + registered upcasters (ADR-010/090)"] -.-> BUS

    classDef evt fill:#e6f4ea,stroke:#34a853,color:#111
    class AGG,SAVE,EBUS,PROC,DISP,BUS,CONSUMER,HANDLER evt

7. Modular monolith → extractable services (ADR-006 / 007 / 008 / 012)

Modules implement IModule and are discovered + Kahn-ordered by ModuleLoader (ADR-059). The same module code runs as a single monolith host or as N service processes behind a YARP gateway, because application code talks to abstractions (IMessageBus, typed gRPC clients) and transport lives at the edges. MMCA.ADC has taken the second path all the way: its four modules each run as their own service host under Source/Services/ behind the Gateway, and the former single MMCA.ADC.WebAPI host is gone. The Gateway's route-to-service map is YARP ReverseProxy configuration, not MapForwarder code (ADR-089), and the Gateway also owns a small set of cross-cutting edge behaviors, composed from the dedicated MMCA.Common.Gateway package: AddMmcaGateway registers services and maps nothing, so LoadFromConfig and MapReverseProxy (and therefore the route table) stay with the host (ADR-088). Deploy A is not hypothetical: it is what MMCA.Helpdesk (its single Tickets module in one API host) runs.

flowchart TD
    subgraph SRC["Same module code (IModule implementations)"]
        MC["ConferenceModule"]
        ME["EngagementModule"]
        MI["IdentityModule"]
        MN["NotificationModule"]
    end
    LOADER["ModuleLoader: discover + Kahn topological order (G14)"]

    MONO["Deploy A: single monolith host<br/>(all modules in one process)"]

    subgraph SERVICES["Deploy B: extracted services (what ADC runs)"]
        direction TB
        GW["YARP Gateway: MMCA.Common.Gateway edge kit (ADR-088),<br/>route map owned by config (ADR-089)"]
        SVC1["Conference service host"]
        SVC2["Engagement service host"]
        SVC3["Identity service host"]
        SVC4["Notification service host<br/>(SignalR hub + gRPC live-channel ingress)"]
        GW --> SVC1
        GW --> SVC2
        GW --> SVC3
        GW --> SVC4
    end

    SYNC["Sync calls: typed gRPC clients + .Contracts<br/>Result-over-the-wire (ADR-007)"]
    ASYNC["Async: IMessageBus over broker (ADR-003/006)"]

    SRC --> LOADER
    LOADER --> MONO
    LOADER --> SERVICES
    SVC1 <-->|"gRPC"| SYNC
    SVC1 <-->|"events"| ASYNC
    NOTE["Transport choice is config, not a rewrite (ADR-008/012)"]
    NOTE -.-> LOADER

    classDef mod fill:#e8f0fe,stroke:#4285f4,color:#111
    classDef dep fill:#e6f4ea,stroke:#34a853,color:#111
    class MC,ME,MI,MN mod
    class MONO,GW,SVC1,SVC2,SVC3,SVC4 dep

8. Persistence, database-per-service + polyglot engines (ADR-006 / 018 / 030)

One concrete SQLServerDbContext over the abstract ApplicationDbContext, one instance per database. Each entity is engine-agnostic; a single [UseDataSource(engine)] attribute on its config class picks SQL Server, Cosmos, or SQLite. Cross-source relationships auto-degrade; the outbox is the cross-source consistency mechanism. Each service self-applies its EF migrations at boot (ADR-030).

flowchart TD
    ENTITY["Domain entity (plain class, no persistence choice)"]
    CFG["Per-entity Configuration : EntityTypeConfiguration&lt;TEntity,TId&gt;"]
    ATTR["UseDataSource(engine) attribute"]
    REG["EntityDataSourceRegistry: resolves engine up front"]

    subgraph SHIMS["Engine shim base classes (one token = one engine)"]
        SQL["…SQLServer&lt;T,Id&gt;<br/>(all prod configs today)"]
        COS["…Cosmos&lt;T,Id&gt;<br/>(shipped + tested, staged)"]
        LITE["…Sqlite&lt;T,Id&gt;<br/>(fast integration tests, staged)"]
    end

    subgraph DBS["Database-per-service (ADR-006)"]
        CTX1["SQLServerDbContext instance: Conference DB + outbox"]
        CTX2["SQLServerDbContext instance: Engagement DB + outbox"]
        CTX3["SQLServerDbContext instance: Identity DB + outbox"]
        CTX4["SQLServerDbContext instance: Notification DB + outbox"]
    end

    XSPEC["CrossSourceSpecification: translatable cross-source filter"]
    MIG["Startup sole-migrator: DatabaseInitStrategy=Migrate (ADR-030)"]

    ENTITY --> CFG --> ATTR --> REG
    REG --> SQL & COS & LITE
    SQL --> DBS
    DBS -->|"FK dropped across sources → batch loaders"| XSPEC
    MIG -.-> DBS

    classDef p fill:#fef7e0,stroke:#f9ab00,color:#111
    class ENTITY,CFG,ATTR,REG,XSPEC,MIG p

9. Authentication & Authorization stack

The auth concern (G08) spans token validation, session cookies, federated sign-in, password hashing, brute-force protection, per-device refresh-session rotation and revocation, a cache-backed forgot/reset-password vertical, and a layered authorization model: RBAC roles → opt-in permissions → resource ownership.

flowchart TD
    subgraph AUTHN["Authentication"]
        JWT["JWT bearer validation"]
        JWKS["JWKS discovery + fallback fetch (ADR-004)"]
        COOKIE["HttpOnly session cookie + non-validating SSR scheme (ADR-022)"]
        HASH["PBKDF2-HMAC-SHA512, 32-byte salt, 600k iters:<br/>one algorithm, no legacy branch (ADR-102)"]
        LOGIN["ILoginProtectionService: lockout + per-IP cap (ADR-029)"]
        EXT["External OAuth: Google / GitHub behind a short-lived<br/>ExternalLogin cookie + single-use code (ADR-036/043)"]
        ROT["AuthenticationServiceBase: 15-min access token +<br/>one refresh session per device, SHA-256 at rest,<br/>rotated with reuse detection (ADR-097);<br/>ITokenRefresher per head (ADR-051)"]
        RESET["Forgot/reset password: cache-backed single-use<br/>hashed token + email leg (ADR-091)"]
    end

    CU["ICurrentUser / claims principal"]
    REV["SoftDeletedUserMiddleware: 401 for a soft-deleted<br/>caller, 30s cached check (ADR-047)"]

    subgraph AUTHZ["Authorization (layered)"]
        RBAC["RBAC roles (RoleValue base)"]
        PERM["HasPermission(x) attribute → perm:x policy<br/>over IPermissionRegistry (ADR-020)"]
        OWN["OwnerOrAdminFilter / OwnershipHelper<br/>row-scope a single resource (ADR-033)"]
    end

    RL["Global rate limiter: authenticated-only,<br/>infra/anon exempt (ADR-019)"]

    JWT --> CU
    JWKS --> JWT
    COOKIE --> CU
    HASH --> LOGIN
    RESET --> HASH
    LOGIN --> ROT
    EXT --> ROT
    ROT --> JWT
    CU --> REV
    REV --> RBAC --> PERM --> OWN
    CU --> RL

    classDef a fill:#fce8e6,stroke:#ea4335,color:#111
    class JWT,JWKS,COOKIE,HASH,LOGIN,EXT,ROT,RESET,REV,RBAC,PERM,OWN,RL a

10. Notifications, three channels behind one send pipeline (ADR-024 / 044)

One use case (SendPushNotificationHandler) writes a durable per-user inbox (optionally scoped to an event via ScopeKey), fires a transient SignalR push, and then an OS-level native push that reaches a backgrounded or killed app. The inbox is the source of truth, so both push legs are non-fatal: a failed SignalR send marks the audit row failed, a failed native send is only logged. Recipient providers resolve who gets notified; the thin MMCA.ADC.Notification module hosts the hub. Email is a separate framework leg (IEmailSender / SmtpEmailSender) driven by its own handlers (Store order mail, the framework's password-reset mail), not something the push sender fans out to, and the same hub also carries ADR-039's deliberately non-durable live-channel events.

flowchart TD
    SRC["Domain / integration event (e.g. new session, bookmark)"]
    RP["INotificationRecipientProvider: resolve target users"]
    UC["SendPushNotificationHandler: one use case, three legs"]
    DURABLE[("UserNotification inbox: durable, persisted,<br/>source of truth, event-scoped via ScopeKey")]
    PUSH["IPushNotificationSender → SignalR<br/>transient, optional Redis backplane for scale-out"]
    NATIVE["INativePushSender → Azure Notification Hubs<br/>FCM v1 / APNs, best-effort (ADR-044)"]
    LIVE["ILiveChannelPublisher: ephemeral channel events,<br/>same hub, never persisted (ADR-039)"]
    EMAIL["IEmailSender / SmtpEmailSender: separate leg"]
    UIBELL["UI: notification bell / in-app inbox (G15)"]

    SRC --> RP --> UC
    UC --> DURABLE
    UC --> PUSH
    UC --> NATIVE
    SRC -.->|"own handlers"| EMAIL
    LIVE -.->|"same NotificationHub"| PUSH
    DURABLE --> UIBELL
    PUSH --> UIBELL

    classDef n fill:#e6f4ea,stroke:#34a853,color:#111
    class SRC,RP,UC,DURABLE,PUSH,NATIVE,LIVE,EMAIL,UIBELL n

11. UI, write-once render everywhere + i18n + theming

A page is authored once as a Razor component in a per-module UI library; both the Blazor web host (Server + WASM) and the .NET MAUI host reference the same libraries, so it renders across Web, Android, iOS, macOS, Windows. InteractiveAuto is declared once on each web head's root router with prerendering left on (ADR-056), so no page carries its own @rendermode. Anything a browser cannot do is a per-capability contract in MMCA.Common.UI with MAUI-native, browser-JS and inert fallback adapters chosen at DI composition time (ADR-042, taught in G26). Culture cookie + IStringLocalizer drive i18n (ADR-027); ThemeService drives day/dark (ADR-028). Each module's UI plugs into one shared shell by contributing navigation and routes through IUIModule (ADR-067).

flowchart TD
    PAGE["Razor component authored once<br/>(per-module .UI library, e.g. EventList.razor)"]
    COMMONUI["MMCA.Common.UI: MudBlazor building blocks,<br/>DataGridListPageBase + EntityServiceBase client<br/>data access, common pages/services (G15, ADR-094)"]
    SHELL["Shared shell + MainLayout: modules contribute<br/>nav + routes via IUIModule (ADR-067)"]

    subgraph HOSTS["Same UI libraries referenced by every host"]
        WEB["Web host: Blazor Server + WebAssembly"]
        MAUI["MAUI host: BlazorWebView"]
    end

    subgraph TARGETS["Renders on"]
        BROWSER["Web browser"]
        AND["Android"]
        IOS["iOS"]
        MAC["macOS"]
        WIN["Windows"]
    end

    I18N["i18n: culture cookie (source of truth) →<br/>IStringLocalizer + .resx; edge errors localized by Error.Code (ADR-027)"]
    THEME["ThemeService binds MudThemeProvider IsDarkMode;<br/>cookie/localStorage/PreferredTheme (ADR-028)"]
    CSP["Security headers + pluggable CSP (ADR-023)"]
    RM["Render mode: InteractiveAuto on the root router,<br/>prerender on (ADR-056)"]
    CAP["Device capability contracts in MMCA.Common.UI:<br/>browser-JS, inert fallback and MAUI-native adapters<br/>chosen per host at DI time (ADR-042, G26)"]

    PAGE --> COMMONUI
    COMMONUI --> WEB
    COMMONUI --> MAUI
    WEB --> BROWSER
    MAUI --> AND & IOS & MAC & WIN
    SHELL -.-> COMMONUI
    I18N -.-> COMMONUI
    THEME -.-> COMMONUI
    CSP -.-> WEB
    RM -.-> WEB
    COMMONUI --> CAP
    CAP -.->|"browser + fallback adapters"| WEB
    CAP -.->|"MMCA.Common.UI.Maui adapters"| MAUI

    classDef u fill:#f3e8fd,stroke:#a142f4,color:#111
    class PAGE,COMMONUI,SHELL,WEB,MAUI,CAP u

12. ADC business modules, bounded contexts end-to-end

Each ADC module is a vertical slice through all layers. Conference is large enough to split across five chapters (G17-G21); Engagement takes two (G22 the bookmark, QR badge check-in and points slices, ADR-072; G23 the conference-day live layer); Identity is one (G24); Notification is the thin host over the Common notifications capability, taught in G10.

flowchart LR
    subgraph CONF["Conference (G17-G21)"]
        direction TB
        C_D["Domain: Event/Session/Speaker/Category/Question/<br/>Sponsor/Activity aggregates"]
        C_A["Application: CQRS handlers, validators, DTOs, Sessionize import, analytics"]
        C_I["Infrastructure: DbContext reg, EF configs, seeding"]
        C_P["API + .Contracts gRPC + extractable service host"]
        C_U["UI: Blazor pages + services"]
        C_D --> C_A --> C_I --> C_P --> C_U
    end

    subgraph ENG["Engagement (G22-G23)"]
        direction TB
        E["G22: UserSessionBookmark, CheckIn/AttendeeBadge and<br/>PointsEntry/LeaderboardOptIn aggregates → use cases →<br/>persistence → API/contracts/service → feedback,<br/>check-in and leaderboard UI"]
        EL["G23 live layer: LivePoll + SessionQuestion aggregates,<br/>voting, moderated Q&amp;A, Happening Now / presenter UI"]
        E --> EL
    end

    subgraph IDN["Identity (G24)"]
        direction TB
        I["User aggregate → change-password/delete/export →<br/>persistence → API/contracts/service → profile UI"]
    end

    subgraph NOT["Notification (G10 host)"]
        direction TB
        N["Thin module host over the Common notifications capability"]
    end

    HOST["ADC Host / Shell / Cross-module composition (G25)"]

    CONF --> HOST
    ENG --> HOST
    IDN --> HOST
    NOT --> HOST

    IDN -.->|"UserRegistered integration event"| CONF
    CONF -.->|"SpeakerLinkedToUser / SpeakerUnlinkedFromUser"| IDN
    ENG -.->|"bookmark + event-live validation over gRPC"| CONF
    CONF -.->|"bookmark counts over gRPC"| ENG
    ENG -.->|"live-channel push over gRPC"| NOT

    classDef adc fill:#e6f4ea,stroke:#34a853,color:#111
    class C_D,C_A,C_I,C_P,C_U,E,EL,I,N,HOST adc

13. The ADRs, grouped by theme

Every accepted ADR in Website/docs-src/adr/, clustered by the concern it governs. That directory's README.md is the canonical index and owns the count and range; this map only regroups it. (Three are struck through there: 011 superseded by 027, 032 by 102, and 050 by 097. They stay on this map because the records they replaced still explain the shape of the code that followed.)

mindmap
  root(("ADRs by theme"))
    Domain and errors
      013 Result pattern
      005 Soft-delete vs erasure
      001 Manual DTO mapping
      048 Primitive identifier aliases
      085 Identifier aliases revisited
      068 Value objects as validated primitives
      104 Plain enums by default, smart enum opt-in
    CQRS and events
      014 CQRS decorator pipeline
      003 Outbox dual-dispatch
      100 Outbox resolved from the messaging mode
      010 Integration event schema versioning
      090 Event upcaster registration
      021 Consumer inbox idempotency
      066 Broker transport selection
      087 Broker poison-message handling
      083 CRUD lifecycle event taxonomy
      054 Saga compensation and reconciliation
      086 Process manager deferred
      052 Background job execution
      074 Recurring job scheduler
    Notifications and real time
      024 Two-channel notifications
      039 Live channel push
      044 Native push delivery
    Data and persistence
      006 Database-per-service
      018 Polyglot persistence
      030 Startup sole-migrator
      002 Navigation populators
      035 Optimistic concurrency via RowVersion
      055 Repository and specification contract
      095 Uniqueness under soft delete
      037 Field-level encryption at rest
      045 Managed file storage and avatars
      057 Expand and contract schema gate
      073 Multi-tenancy model
      075 Audit trail
      076 Data-subject export
    Services and transport
      007 gRPC extraction
      008 Service-extraction topology
      089 Gateway topology as configuration
      088 Gateway edge responsibilities
      012 gRPC host transport
      009 Resilience and RTO/RPO
      059 IModule contract and composition
    Security and auth
      004 JWKS dual-fetch
      020 Permission-based authz
      022 Session-cookie auth
      029 Brute-force protection
      102 PBKDF2-only password hashing
      032 Password hashing superseded by 102
      033 Resource-ownership authz
      019 Rate limiting
      023 Security headers and CSP
      036 External OAuth login
      047 Soft-deleted-user session revocation
      097 Multi-device refresh sessions
      050 JWT and rotating refresh token superseded by 097
      051 Client auth token lifecycle
      091 Cache-backed password reset
      069 Shared data-protection key ring
      082 Two-tier CORS posture
      061 Runtime secret management
    API edge
      017 Request idempotency
      031 Feature-flag management
      034 Generic entity controllers
      099 Generic write-side entity commands
      079 Shared HTTP middleware pipeline
      040 Authenticated output caching
      046 HTTP API versioning
      078 CSV export endpoint
      084 Stripe webhook ingress
    Runtime and operations
      025 Startup warm-up and readiness
      070 Fail-fast configuration contract
      026 Two-tier caching
      077 HybridCache substrate
      041 Observability and telemetry
      098 Aspire for orchestration, not testing or dashboards
      062 SLO alerting as code
      064 Deploy recency gates
      080 Rollout and revision rollback
      081 Cost baseline deploy gate
      093 Container image posture
      096 Best-effort side-effect contract
    Front-end
      027 Multi-locale i18n
      028 Day and Dark theme
      056 Blazor render-mode strategy
      067 UI module shell composition
      094 Client entity data access
      063 Accessibility conformance gate
      092 Web vitals budget gate
      011 en-US-only i18n superseded by 027
    Mobile and device
      042 Device capability abstraction
      043 Mobile deep links and native OAuth callback
      071 Barcode scanning and QR display
      072 QR badge check-in and points
    Governance
      015 Architecture fitness functions
      016 Lockstep versioning + MassTransit v8 pin
      038 Supply-chain provenance
      049 Library ConfigureAwait policy
      053 Dual-registry package publishing
      101 MMCA.Common metapackage for the Core 6
      058 Runtime conformance suites as a package
      103 bUnit component-test tier as a package
      065 Scaffolding templates
      060 Performance-regression gate
      105 Data residency as a build gate
      106 Extension members as the public DI surface

14. The 34-category evaluation rubric

The lens the guide tags code against ([Rubric §N]). Scored on two axes: Maturity (0-4, process) and Implementation (0-10, substance). Three parts.

mindmap
  root(("34-category rubric"))
    Part A App and Backend 1-17
      1 SOLID
      2 Design Patterns
      3 Clean Architecture
      4 Domain-Driven Design
      5 Vertical Slice
      6 CQRS and Event-Driven
      7 Microservices Readiness
      8 Data Architecture
      9 API and Contract Design
      10 Messaging and Integration
      11 Security
      12 Performance and Scalability
      13 Observability
      14 Testability
      15 Code Quality
      16 AI-Native Architecture
      17 DevOps and Deployment
    Part B Front-End and UI 18-28
      18 UI Architecture
      19 State Management
      20 Design System and Theming
      21 Accessibility
      22 Responsive and Cross-Device
      23 Front-End Performance
      24 Forms and UX Safety
      25 Navigation and IA
      26 Front-End Security
      27 Internationalization
      28 Front-End Testing
    Part C Ops and Governance 29-34
      29 Resilience and Continuity
      30 Compliance and Privacy
      31 Cost Efficiency and FinOps
      32 Dependency and Supply-Chain
      33 Developer Experience
      34 Architecture Governance

15. How the axes fit together (reading map)

The guide is organized on two axes at once. This ties the diagrams above back to the guide's navigation.

flowchart TD
    PRIMER["Primer: cross-cutting concepts, stack, conventions, rubric (taught once)"]

    subgraph AXIS1["Primary axis: functional groups (G01→G27)"]
        FW["Framework groups G01-G16 (MMCA.Common)"]
        ADCG["ADC module groups G17-G25"]
        CAPG["Device capability layer G26 (MMCA.Common, appended late)"]
        TESTG["Testing G27"]
        FW --> ADCG --> CAPG --> TESTG
    end

    AXIS2["Secondary axis: dependency Level within each group<br/>(meet a type only after its first-party deps)"]

    LENS["Rubric §1-§34 tags woven inline + ADR-00N cross-refs"]
    DEVOPS["DevOps chapters: CI/CD · IaC · Aspire · runbooks · testing"]

    PRIMER --> AXIS1
    PRIMER --> LENS
    AXIS1 --> AXIS2
    AXIS1 --> DEVOPS

    classDef m fill:#e8f0fe,stroke:#4285f4,color:#111
    class PRIMER,FW,ADCG,CAPG,TESTG,AXIS2,LENS,DEVOPS m

Notes on fidelity

  • Group-to-group arrows in §3 show the dominant "builds on" direction from the charters and Level ranges in 00-group-taxonomy.md; the guide allows forward references where functional cohesion outranks strict layering, so a few minor edges are omitted for readability.
  • GNN here is the chapter number (group-NN-*.md), the numbering the index and the reading paths use. 00-group-taxonomy.md still carries the original classification ids for the chapters inserted later (its G26 is the live layer, chapter 23; its G27 is the device capability layer, chapter 26; its G25 is testing, chapter 27), so match that table by chapter filename rather than by its id.
  • Pattern diagrams (§4-§12) reflect the mechanisms as taught in the corresponding group-NN-*.md chapters and the ADRs named in 00-primer.md.
  • The 36 dependency cycles (SCCs) listed in the manifest are kept whole inside a single group (e.g. the eight-member ApplicationDbContext ↔ interceptors ↔ OutboxFinalizer cycle in G07, the Conference aggregate nav-cycles in G17, the LivePollLivePollOption cycle in the live-layer chapter); they are not drawn as separate nodes here.