Architecture governance

MMCA.Store — Architecture Scorecard

Canonical, version-controlled scorecard for this repo — the single source of truth for MMCA.Store's architecture scores. This is Store's first in-repo governance artifact (previously its posture lived only in the workspace docs + memory). Scored against the rubric at ../MMCA.Common/ArchitectureEvaluationCriteria.md; framework-wide facts in ../MMCA.Common/FACTS.md. Remediation lives in RemediationBacklog.md; the cross-repo comparison in ../Docs/Architecture/CrossRepoComparison.md.

Rubric: ../MMCA.Common/ArchitectureEvaluationCriteria.md • Date: 2026-07-17 • Two axes per category: Maturity (0-4, process/governance) and Implementation (0-10, substance/execution). Indices computed deterministically from the scores below. Verified against current source at HEAD 89e4a04 (clean tree); framework dependency pinned at MMCA.Common. v1.117.0* (all 15 packages, lockstep, confirmed no divergence; ADR set 001-048 lives in MMCA.Common). What moved this cycle (2026-07-03 drift-plan execution, D1/D4/D5/D8/D9/D10): §21 Accessibility M3→4 and §28 Front-End Testing M3→4 (the Playwright + axe suite now gates the deploy: e2e-gate joined deploy.yml's needs after two consecutive fully green E2E runs, 28682334766 chromium 83/83 with firefox + webkit also green, confirmed by 28683063228), §12 Performance I7→8 (client Web Vitals are now measured in CI: WebVitalsTests writes LCP/CLS/TTFB/FCP artifacts per run), §23 Front-End Performance I6→8 (the public CatalogBrowse moved to server-side paging via GetPagedAsync + bounded MobileInfiniteScrollList, and cart enrichment now uses a targeted by-variant-id batch lookup instead of fetching the whole product list), and §32 Supply-Chain I7→8 (all three CI restores run --locked-mode and the suppress-aware vulnerability audit is now gating, D8/D9). The prior cycle's moves (2026-07-02 docs sweep: §16/§25/§20 M3→4, §27 scored M4/I7, §14 I6→9, §34 I7→9) are retained in the rows below. A same-day i18n completion sweep (2026-07-03, ADR-027 Decision 9) then lifted §27 Implementation 7→8 (zero residual literals incl. the cart/checkout/Stripe snackbars, dual CI gates, MudBlazor chrome + nav localized; indices Implementation 80.3%→80.4%). A subsequent 2026-07-11 drift-convergence cycle (drift plan D1-D13, pin v1.113.0) moved six scores: §1 SOLID Implementation 8→9 (the ctor-dependency-ceiling gate ConstructorDependencyCountTests + TimeProvider injection, D9), §9 API Implementation 8→9 (the v2 ServiceInfoController + two deploy-gating Contract tests, D12), §24 Forms Maturity 3→4 (the CI-gated FormsConventionTests, D11), §28 Front-End Testing Implementation 6→8 (bUnit breadth grown to 214 facts across 40 files, D7), §29 Resilience Implementation 8→9 (the dr-freshness deploy gate + weekly dr-drill cron + GracefulShutdownTests, D3), and §21 Accessibility Maturity 4→3 with Implementation 7→8 (honest reconciliation to ADC's M3: 22 axe scans + the new screen-reader runbook, but no dated SR pass yet, D6). D2 (MI-SQL activation wiring) and D4 (cost-guard deploy gate) also landed, with no §17/§31 score move. Indices Maturity 94.4%→94.1%, Implementation 80.4%→82.5%. A 2026-07-16 full re-score (34 categories, two-pass with adversarial verification) moved three scores: §13 Observability Implementation 8→9 (both prior deductions closed: the SLO workbook is provisioned in IaC at infra/main.bicep:274 and the per-alert infra/OPERATIONS.md runbook is in-repo; Maturity holds 3 because dashboards/runbooks are IaC/review-enforced, not CI-gated), and §18 UI Architecture + §19 State Management Maturity 3→4 (the sealed UIArchitectureConventionTests and StateManagementConventionTests subclasses of the shared v1.116.0 fitness bases run non-vacuously in the deploy-gating MMCA.Store.CI.slnf on every push and PR, the same mechanism that earned ADC its M4; their proposed Implementation bumps were adversarially rejected as enforcement gains mis-posted to the substance axis). The same re-score DECLINED the recorded maturity candidacies on §12 (k6 stays monthly/on-demand, not a merge gate) and §22 (firefox/webkit are still continue-on-error in e2e.yml:71, contrary to the backlog's promotion claim), and held the §20/§24/§27 impl candidacies. §17 Implementation 8→9 additionally banked on directly verified evidence: MI-SQL is active in production (repo variable USE_MANAGED_IDENTITY_SQL=true since 2026-07-12, activation deploy 29192048197 green), correcting the row's stale inert claim. Indices Maturity 94.1%→95.9%, Implementation 82.5%→83.0%. A same-day drift-analysis fold (2026-07-16, cross-repo ADC-vs-Store comparison, each move adversarially verified) moved two more scores: §23 Maturity 3→4 (the CWV budgets are hard assertions in the deploy-gating chromium e2e-gate, the identical evidence ADC's twentieth cycle credited; the earlier same-day hold at M3 had wrongly imported §12's k6-cadence reasoning) and §32 Implementation 8→9 (capability-level parity with ADC's I9: identical --locked-mode/audit/SBOM gating; the earlier FLAG reasoned from stale scorecard text, not capability). Doc corrections in the same fold: §16's narrated pin 1.113.0→1.116.0, §32's lock-file count 49→55, and the README gained the ADC-parity broker note (§33). Indices Maturity 95.9%→96.6%, Implementation 83.0%→83.3%. A 2026-07-17 full re-score (34 categories, two-pass with adversarial verification, pin v1.117.0) moved four scores: §5 Vertical Slice M3→4 and I7→8 (the sealed SliceCohesionTests subclass runs non-vacuously in the deploy-gating MMCA.Store.CI.slnf, the identical gate ADC credits at M4/I8; the layered-by-project hybrid stays a deliberate implementation-axis cap, no longer a maturity deduction), §13 Observability M3→4 (ObservabilityConventionTests machine-enforces the sloAlertSpecs-to-OPERATIONS.md pairing in the CI merge gate, closing exactly the "not CI-gated" reasoning that held M3), §22 Responsive M3→4 (the 2026-07-16 gate flip verified live: continue-on-error in e2e.yml is scoped to scheduled non-chromium runs only, so all three engines the e2e-gate invokes can fail a deploy), and §27 i18n I8→9 (the PseudoLocalizationTests candidacy granted: pseudo-loc sentinel, no-overflow, and en-US leak probes run over Store's own /, /catalog, and /login pages in the deploy-gating chromium e2e-gate). The narrated framework pin refreshed 1.116.0→1.117.0 throughout. Indices Maturity 96.6%→98.4%, Implementation 83.3%→83.6%.

Executive summary

MMCA.Store is a .NET 10.0 (LangVersion preview) DDD/Clean Architecture e-commerce system (Catalog, Sales, Identity modules; Stripe checkout) extracted into independently-hosted services behind a YARP gateway, collaborating via Result-over-the-wire gRPC and MassTransit integration events on the outbox pattern. It consumes the shared MMCA.Common.* framework at v1.117.0 in lockstep with MMCA.ADC. Architecturally it is at ADC parity — and one prior assumption is corrected here: Store runs database-per-service (Store_Catalog/Store_Sales/Store_Identity, each with its own dbo.OutboxMessages; the legacy single MMCAStore DB is retained read-only as an archive/rollback only), not a single shared database. Its tactical depth (Clean Architecture, DDD, CQRS, the decorator pipeline, soft-delete/audit, RowVersion concurrency) is inherited framework substance, enforced by 23 NetArchTest fitness-test classes (shared *TestsBase subclasses from MMCA.Common.Testing.Architecture plus Store-local guards such as DataResidencyTests, PiiConventionTests, and IntegrationEventContractTests, ADR-015; the compile-time layer-guard MSBuild target is MMCA.Common-internal and does not run here).

The two axes are asymmetric: Maturity 98.4% vs Implementation 83.6%. Implementation is the weaker axis by ~15 points, a wider gap than ADC's. The maturity is high because Store inherits the framework's governed mechanisms and adds a strong operational floor (two-phase Bicep IaC, OIDC + Key Vault managed identity, a post-deploy smoke gate with auto-rollback, a drilled DR restore, a cost-guard surge-drift check, a k6 load test, and now a chromium E2E/axe deploy gate at ADC parity). The former §21/§28 gate gap and the §23 catalog fetch-all are closed this cycle, and the §27 residual unlocalized strings were closed by the same-day i18n completion sweep (I7→8; zero literals, dual CI gates); the former §18/§19 review-only maturity gap closed on 2026-07-16 (both are now CI-enforced by the shared convention fitness gates at M4), and the same-day drift-fold closed §23 (the deploy-gated CWV budget assertions credited at M4, ADC parity); the 2026-07-17 re-score then closed §5 (slice cohesion CI-gated, the hybrid kept as an implementation cap), §13 (the alert-to-runbook pairing gate), and §22 (the three-engine gate flip verified live), leaving §12 (k6 not a merge gate) and §21 (screen-reader pass pending) as the only below-4 maturity categories. §14's former coverage gap is closed: the deploy-gating floor is 51.6 measured on Store's own code (+MMCA.Store.*;-*.Tests, ~54% actual). Supply-chain (§32): the vulnerability gate is NuGetAudit + TreatWarningsAsErrors at restore plus the gating suppress-aware audit, the SBOM is a hard gate, and all three CI restores run --locked-mode.

Front-end security is the standout (§26, impl 9): access token in-memory, refresh token in an HttpOnly cookie (no localStorage), a hardened origin-pinned CSP, and runtime-config fetch (no secrets in the bundle), confirmed at ADC parity. No category is N/A: §27 Internationalization is scored (M4/I9 after the 2026-07-03 completion sweep and the 2026-07-17 pseudo-loc layout-tolerance grant) since the ADR-027 en-US + es localization shipped with its CI-gated translation-completeness fitness function, superseding ADR-011's single-locale exclusion; the sweep added the LocalizedTextConventionTests literal gate and removed every residual hard-coded string.

Scorecard

# Category Weight Maturity (0-4) Impl (0-10) Weighted (M·w / I·w) Evidence / Notes
1 SOLID Principles 3 4 9 12/27 Narrow-interface ctor DI, OCP via state strategy (no type-switch); the constructor-dependency ceiling is machine-enforced by ConstructorDependencyCountTests (ceiling 7, AuthenticationService at the high-water mark, the rubric 8+-dependency red flag cleared, D7), and TimeProvider is injected over ambient DateTime.UtcNow. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/ConstructorDependencyCountTests.cs:14; AuthenticationService.cs:25; OrdersController.cs:36-47; Order.cs:43-53 (FrozenDictionary<OrderStatus,IOrderState>)
2 Design Patterns 2 4 9 8/18 Factory→Result, payment State machine, Saga (Stripe compensation), Specification, Decorator, Outbox, Repository/UoW — idiomatic, named. — Order.cs:76-106,43-53; Orders/Saga/OrderCancelledSagaHandler.cs; Orders/Specifications/OrdersByCustomerSpecification.cs
3 Clean Architecture 3 4 9 12/27 NetArchTest-enforced layer rules; domain framework-pure. (The compile-time layer-guard MSBuild target is MMCA.Common-internal; it is not a Store-side gate.) Evidence: StoreArchitectureMap.cs:14-43 + LayerDependencyTests/DomainPurityTests; Order.cs:1-13 (imports only Common.Domain/Shared)
4 Domain-Driven Design 3 4 9 12/27 Aggregate root, Money VO, by-id cross-aggregate refs, domain events, invariants, factory→Result, rich state behavior; identifier aliases. — Order.cs (Money :32, OrderInvariants, OrderIdentifierType)
5 Vertical Slice Architecture 2 4 8 8/16 Cohesive command+handler+request+validator+mapper per operation; deliberate layered-by-project hybrid (cross-cutting in the pipeline). ↑ Maturity 3→4 + Implementation 7→8 (2026-07-17): slice cohesion is machine-enforced pre-merge by SliceCohesionTests (sealed subclass of the shared non-vacuous SliceCohesionTestsBase, two real rule facts) in the deploy-gating MMCA.Store.CI.slnf, the identical gate ADC credits at M4/I8; the hybrid stays a deliberate implementation-axis cap (holds impl at 8, not a maturity deduction). Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/SliceCohesionTests.cs:9; MMCA.Store.CI.slnf:52; Application/Orders/UseCases/Cancel/{CancelOrderCommand,CancelOrderHandler}.cs
6 CQRS & Event-Driven 2 4 8 8/16 Command/query split, outbox persist-then-publish, idempotent inbox (AddInboxMessages), Stripe saga; one cross-service event flow (ProductVariantChanged) + a deliberate in-process UserRegistered domain event; the genuine outbox-to-broker-to-consumer round-trip is now covered by the non-gating nightly MMCA.Store.CrossService.IntegrationTests (Testcontainers RabbitMQ+SQL, D5). Evidence: Sales.Service/Program.cs:168-169; IntegrationEventContractTests.cs; Tests/Integration/MMCA.Store.CrossService.IntegrationTests/
7 Microservices Readiness 3 4 8 12/24 Database-per-service (corrected from the old single-DB assumption) + per-source outbox, async events + versioned gRPC, Polly resilience, extractable modules, 5 independently-built images. Minor: all per-service DBs share one physical SQL server. Evidence: infra/main.bicep:396-418 (per-service DBs) + :445-474 (Service Bus broker); Sales.Service/Program.cs:156-157,168
8 Data Architecture 3 4 8 12/24 Per-aggregate tx, soft-delete + filtered indexes, central audit (Common), RowVersion concurrency, versioned per-service migrations with a model-drift CI gate, LTR backups. — ConcurrencyConventionTests.cs + AddRowVersionToAllEntities; deploy.yml:86-99; main.bicep:383-394
9 API & Contract Design 2 4 9 8/18 RFC 9457 Problem Details, header versioning, pagination, DTO decoupling (ADR-001), gRPC .proto, OpenAPI served non-prod, and a demonstrated v2 contract: ServiceInfoController carries [ApiVersion("1.0", Deprecated)] + [ApiVersion("2.0")], backed by two deploy-gating contract guards (D12). Below 10: OpenAPI not exposed in prod (internal behind gateway); v2 demonstrated on one endpoint. Evidence: Catalog.API/Controllers/ServiceInfoController.cs:18; Tests/Integration/MMCA.Store.Catalog.IntegrationTests/Contract/{ApiVersioningTests,OpenApiContractTests}.cs; OrdersController.cs:77,34,114; Sales.Service/Program.cs:102,193
10 Cross-Cutting Concerns 2 4 8 8/16 Pipeline decorators, typed ValidateOnStart options, CORS/versioning/rate-limit/output-cache/compression, shared Polly — none copy-pasted. — Sales.Service/Program.cs:73-76,88-96,171
11 Security 3 4 8 12/24 RS256/JWKS (no shared secret), server-side resource authz (404-not-403), KV secrets via UAMI, EF parameterized, Stripe webhook-secret handling. — main.bicep:475-528,457-460; Sales.Service/Program.cs:116-118; OrdersController.cs:35,143,190,291-317
12 Performance & Scalability 2 3 8 6/16 Async throughout, projections/AsNoTracking/paging on hot paths, tiered cache (output + Redis), stateless scale-out, a real k6 load test, and client Web Vitals measured per E2E run (LCP/CLS/TTFB/FCP written as CI artifacts, D10). Evidence: Tests/Load/k6/catalog-read-load.js + load-test.yml; Tests/E2E/MMCA.Store.E2E.Tests/Workflows/WebVitalsTests.cs + e2e.yml (WEB_VITALS_OUTPUT_DIR); OrdersController.cs:73,108
13 Observability & Operability 2 4 9 8/18 OTel logs/traces/RED metrics via ServiceDefaults (incl. MMCA.Common.Outbox), /health+/alive+/health/ready, App Insights + 3 SLO alerts + action group + saved SLO workbook provisioned in IaC, per-alert operations runbook in-repo, correlation, poll-span noise control, CI-gated graceful shutdown. ↑ Maturity 3→4 (2026-07-17): the alert-to-runbook pairing is now a CI-gated fitness function: ObservabilityConventionTests parses the embedded infra/main.bicep sloAlertSpecs and fails the merge gate on any alert without a severity-correct OPERATIONS.md section (3-spec non-vacuity floor), closing exactly the "IaC/review-enforced, not CI-gated" reasoning that held M3. Implementation 9 (2026-07-16: workbook + runbook deductions closed). Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/ObservabilityConventionTests.cs:24,34; MMCA.Store.CI.slnf:52; main.bicep:204 (sloAlertSpecs), :274 (workbook); infra/OPERATIONS.md:16; Tests/Hosts/MMCA.Store.Gateway.Tests/GracefulShutdownTests.cs:15
14 Testability & Test Strategy 3 4 9 12/27 Non-inverted pyramid + arch fitness + integration tier gating deploy; the unit-tier coverage floor is 51.6 measured on Store's own code (reportgenerator +MMCA.Store.*;-*.Tests, ~54% actual), the same self-filtered gate shape as ADC. A non-gating nightly real-broker round-trip tier (MMCA.Store.CrossService.IntegrationTests, Testcontainers RabbitMQ+SQL, cross-service-tests.yml) now covers the outbox-to-broker-to-consumer flow the in-process tests only approximate (D5). Held at 9 (not 10): the broker round-trip is non-gating nightly and the SQL integration suite runs only in CI. Evidence: deploy.yml:69-99 (floor 51.6 at :86, self-filter at :84), :147-215; arch tests; Common.Testing.E2E
15 Best Practices & Code Quality 2 4 8 8/16 Five analyzers at error + TWAE + CPM; targeted vuln pin; consistent Result pattern. Evidence: Directory.Packages.props:85-88,97 (the five analyzers), :130 (targeted OpenTelemetry.Api vuln pin)
16 Maintainability & Evolvability 2 4 8 8/16 Versioned framework contracts (Common 1.117.0, lockstep) with the lockstep invariant executable: FrameworkVersionConsistencyTests asserts every MMCA.Common.* pin shares one version and fails the build on a partial sweep, running in the CI merge gate. Consumes the shared arch-test package, current CLAUDE.md, extractable modules. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/FrameworkVersionConsistencyTests.cs; MMCA.Store.CI.slnf:52; Directory.Packages.props:8-19
17 DevOps & Deployment 2 4 9 8/18 CI gates (build/analyzers/tests/coverage-floor/model-drift) then an integration gate then deploy with post-deploy smoke + auto-rollback; two-phase Bicep; OIDC managed identity; cost-guard/dr-drill/load-test workflows. ↑ Implementation 8→9 (2026-07-16): MI-SQL is ACTIVE in production, not inert: the full infra/SQL-MANAGED-IDENTITY.md sequence completed 2026-07-12 (repo variable USE_MANAGED_IDENTITY_SQL=true set 2026-07-12, activation deploy run 29192048197 green with the full gate chain + smoke), so all three services authenticate passwordless via managed identity; the SQL password path remains only as the documented dual-auth rollback. Evidence: deploy.yml:452-454,541-553 (MI-SQL wiring); infra/SQL-MANAGED-IDENTITY.md; repo vars USE_MANAGED_IDENTITY_SQL/SQL_AAD_ADMIN_* (verified 2026-07-16)
18 UI Architecture & Components 3 4 8 12/24 Code-behind split, @inherits DataGridListPageBase<ProductDTO>, scoped cart service owns data/behavior, reuse of Common.UI primitives. ↑ Maturity 3→4 (2026-07-16): the container/presentational conventions are machine-enforced pre-merge by UIArchitectureConventionTests (sealed subclass of the shared base, 400-line code-behind cap + 120-line inline @code cap, non-vacuous MinimumCodeBehindFiles guard; largest code-behind is 368 lines), running in the deploy-gating MMCA.Store.CI.slnf on push and PR, the same mechanism as ADC's M4. Implementation holds 8: minor inline-style logic in markup remains. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/UIArchitectureConventionTests.cs:11; StoreArchitectureMap.cs:29,37,45; MMCA.Store.CI.slnf:52; deploy.yml:59,429; ProductList.razor.cs:14; CartDrawer.razor:50
19 State Management & Data Flow 3 4 8 12/24 Single source of truth, scoped (no static cross-user state), unidirectional flow with OnChange+InvokeAsync(StateHasChanged)+Dispose, single-flight token hydrate. ↑ Maturity 3→4 (2026-07-16): both §19 red flags are machine-enforced pre-merge by StateManagementConventionTests (mutable-static-state reflection scan over the Layer.Ui assemblies with a non-vacuous guard, plus the singleton-*StateService source scan), in the deploy-gating MMCA.Store.CI.slnf; CartStateService is registered TryAddScoped, proving the rule the gate enforces. Implementation holds 8. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/StateManagementConventionTests.cs:12; Sales.UI/DependencyInjection.cs:35; MMCA.Store.CI.slnf:52; CartStateService.cs:38; CartButton.razor:33; ServerTokenStorageService.cs:44
20 Design System & UI Consistency 2 4 7 8/14 MudBlazor + Common.UI theme/tokens used consistently, shared grid-paging wrapper; the brand-color token convention is now CI-enforced by BrandColorTokenTests (shipped 5fbd003, guards both UI hosts' home CSS against hard-coded brand hex). Minor: a few remaining inline style= values in cart/catalog markup hold impl at 7. Evidence: App.razor:11; Tests/Architecture/MMCA.Store.Architecture.Tests/BrandColorTokenTests.cs; ProductList.razor:118
21 Accessibility (a11y) 3 3 8 9/24 Strong semantics/ARIA, stated WCAG 2.1 AA, 22 axe scans over the public, shopper, and Catalog/Sales/Identity admin surfaces (broadened from 10, D6), and the axe suite gates the deploy (the chromium e2e-gate in deploy.yml's needs; nightly keeps the full matrix). Maturity is honestly 3, not 4 (corrected D6): the rubric pairs axe-in-CI with a recorded manual screen-reader pass, and the new ACCESSIBILITY-SCREENREADER-PASS.md runbook's results log is still empty, matching ADC's M3 on the same rubric. Implementation 8: the first deploy-gating E2E run with the broadened scans is pending. Evidence: Tests/E2E/MMCA.Store.E2E.Tests/Workflows/AccessibilityTests.cs (22 scans); ACCESSIBILITY-SCREENREADER-PASS.md (runbook, dated pass pending); CartDrawer.razor:9,20,67; deploy.yml (e2e-gate in deploy needs)
22 Responsive & Cross-Browser 2 4 8 8/16 Fluid layouts, grid→mobile-card reflow, defined+Playwright-verified chromium/firefox/webkit matrix. ↑ Maturity 3→4 (2026-07-17): the 2026-07-16 gate flip verified live: continue-on-error is scoped to github.event_name == 'schedule' && matrix.browser != 'chromium', the e2e-gate invokes all three engines, and it sits in deploy.yml's needs, so a firefox/webkit red blocks a deploy (nightly non-chromium legs stay advisory). Evidence: e2e.yml:69,76; deploy.yml:315,429; ProductList.razor:24; CatalogBrowse.razor:86
23 Front-End Performance 2 4 8 8/16 Server paging/debounce/@key/output-cache on admin grids; the public CatalogBrowse uses server-side paging (GetPagedAsync + MudPagination, D4) and cart enrichment resolves names via the targeted by-variant-id batch lookup. ↑ Maturity 3→4 (2026-07-16 drift-fold, adversarially verified): the Core Web Vitals budgets are HARD assertions (WebVitalsTests.cs:76-79, Assert.True per metric, no soft mode) running unfiltered inside the deploy-gating chromium e2e-gate (deploy.yml:429 needs), the identical evidence shape ADC's twentieth cycle credited M4 for; the prior M3 wrongly imported §12's k6-cadence reasoning into a category whose own enforcement is per-deploy. Evidence: Tests/E2E/MMCA.Store.E2E.Tests/Workflows/WebVitalsTests.cs:76-79; deploy.yml:311-315,429; CatalogBrowse.razor.cs; CartStateService.cs
24 Forms, Validation & UX Safety 2 4 8 8/16 Unsaved-changes guard with current-state accessor (9 pages), double-submit blocked, all states designed, destructive confirm, abandoned-payment recovery. ↑ Maturity 3→4 (D11): the four admin create forms' guard/dirty/validated-MudForm/Required markers are machine-enforced by the CI-gated FormsConventionTests (MinimumCreateForms=4), matching ADC on the same evidence. Client validation is MudForm-level (not full FluentValidation parity), the impl 8→9 lever. Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/FormsConventionTests.cs; MMCA.Store.CI.slnf:52; ProductCreate.razor:8,58; CartStateService.cs:232
25 Navigation & Information Arch 2 4 8 8/16 Bookmarkable typed routes, server-enforced [Authorize]/AuthorizeView (not UI-only), 404 handled, breadcrumbs, SSR session-cookie so deep-links/F5 don't bounce; role-guard regression is CI-gated by the three per-module *RouteAuthorizationTests (commit c4adff2), and per-actor navigation is documented in NavigationFlow.md. Evidence: ProductList.razor:3; CatalogRoutePaths; Tests/Modules/{Catalog,Sales,Identity}/MMCA.Store.*.UI.Tests/*RouteAuthorizationTests.cs in MMCA.Store.CI.slnf:39,45,51
26 Front-End Security 3 4 9 12/27 Access token in-memory, refresh token in HttpOnly cookie (no localStorage), hardened origin-pinned CSP (no unsafe-inline script-src in prod, frame-ancestors none), runtime /client-config fetch (no secrets in bundle), UseAuthenticatedNoStore. Evidence (implementation hoisted to Common in the v1.96.0 Move-to-Common wave, consumed byte-identically here): Common MMCA.Common.UI.Web/Services/ServerTokenStorageService.cs; Common MMCA.Common.UI/Services/Auth/WasmTokenStorageService.cs; Common MMCA.Common.UI.Web/Security/BlazorCspPolicyProvider.cs
27 Internationalization (i18n) 1 4 9 4/9 ↑ Implementation 8→9 (2026-07-17): the layout-tolerance lever is realized on Store's own pages: PseudoLocalizationTests activates qps-Ploc via the production /culture/set cookie mechanism and asserts the [!! sentinel, the no-horizontal-overflow expression, and a per-page en-US leak probe over /, /catalog, and /login, riding the deploy-gating chromium e2e-gate (Tests/E2E/MMCA.Store.E2E.Tests/Workflows/PseudoLocalizationTests.cs:57). Prior: Implementation 7→8 (2026-07-03 i18n completion sweep, ADR-027 Decision 9); Maturity 4 holds with a second gate. Full ADR-027 adoption (supersedes ADR-011): en-US + es .resx pairs across all three module UIs, the API error resources, both StoreHome hosts, and the three UI modules' nav items (30 base / 30 .es siblings; ~130 new key pairs on the sweep), UseCommonRequestLocalization + MapCultureEndpoint (UI.Web/Program.cs:107,140), per-module AddErrorResources (Identity.Service/Program.cs:150), User.PreferredCulture + AddUserPreferences migration. The prior deductions are ALL closed: zero hard-coded snackbars (35 sites to whole-sentence page keys, including the cart/checkout/Stripe strings; raw {ex.Message} never surfaces), the ErrorMessages.Success concatenation is gone (obsoleted upstream, all 28 sites swept), 33 literal breadcrumb labels localize from Breadcrumb.* keys built in OnInitialized, nav menus localize via NavItem.TitleResource + new module resx pairs, and MudBlazor built-in chrome localizes via the framework's ResxMudLocalizer (inherited). Maturity 4, now doubly gated in CI.slnf: TranslationCompletenessTests (floor raised 20→25) + the NEW LocalizedTextConventionTests (no hard-coded snackbar/title/<PageTitle>/breadcrumb/NavItem literal can ship; MinimumScannedFiles=40). Evidence: Tests/Architecture/MMCA.Store.Architecture.Tests/{TranslationCompletenessTests.cs,LocalizedTextConventionTests.cs}; Sales.UI/.../CartDrawer.razor.cs + OrderDetail.razor.cs (whole-sentence Snackbar.* keys); CatalogUIModule.cs (TitleResource + resx pair); verification: Store CI suite 1120/1120 green post-sweep
28 Front-End Testing & Quality 3 4 8 12/24 bUnit .UI.Tests gate the deploy (CI.slnf), broad state-coverage component tests (loading/empty/error/edge), page-level render/parameter/event tests, shared Common.Testing.E2E, and the Playwright suite gates the deploy (chromium e2e-gate; firefox/webkit stay advisory on the nightly matrix). ↑ Implementation 6→8 (D7): bUnit breadth grown to 214 [Fact]/[Theory] across 40 files (Catalog 63 / Sales 126 / Identity 25), the full CI gate green at 1393/1393. Evidence: Tests/Modules/{Catalog,Sales,Identity}/MMCA.Store.*.UI.Tests (214 facts / 40 files); deploy.yml (e2e-gate in deploy needs); e2e.yml (workflow_call + browsers input)
29 Resilience & Business Continuity 3 4 9 12/27 RTO/RPO per scenario, PITR(7d geo-redundant)+LTR, a drilled restore (recorded PASS 28.9 min), SLO alerts, post-deploy smoke then rollback, single-region risk explicitly accepted. Maturity 4 legitimately held (D3): a dr-freshness job is in deploy.needs and fails the deploy when the last successful dr-drill is stale, backed by a weekly dr-drill.yml cron; Implementation 8→9 on the CI-gated GracefulShutdownTests in MMCA.Store.Gateway.Tests (closing the graceful-shutdown gap vs ADC). Honest note: the recorded drill narrative in DISASTER-RECOVERY.md dates to 2026-06-22, but the weekly dr-drill.yml cron has since run green (most recently 2026-07-13), so dr-freshness is satisfied. Evidence: .github/workflows/deploy.yml (dr-freshness in deploy.needs); .github/workflows/dr-drill.yml:29 (weekly cron); Tests/Hosts/MMCA.Store.Gateway.Tests/GracefulShutdownTests.cs; infra/DISASTER-RECOVERY.md:146-148
30 Compliance, Privacy & Governance 2 4 8 8/16 Real erasure path (Delete+Anonymize in one UoW, ADR-005), data export, residency + PII enforced by fitness functions, auto-stamped audit fields. — DeleteUserHandler.cs:43-69; Customer.cs:20/User.cs:17 (IAnonymizable); DataResidencyTests.cs; PRIVACY.md:57-85
31 Cost Efficiency / FinOps 2 4 8 8/16 Right-sized Basic per-service DBs, automated surge-drift guard, IaC budget + alerts, telemetry span filtering, resource tags. — cost-guard.yml; infra/main.bicep:80-86,271
32 Dependency & Supply-Chain 2 4 9 8/18 Vulnerability auditing is CI-gated twice: NuGetAudit raises advisory warnings at restore and TreatWarningsAsErrors promotes them to errors (failing build-and-test and every Docker image build), and the supply-chain job's suppress-aware audit now gates (exit 1 on any non-suppressed advisory, D8); accepted advisories are the documented NuGetAuditSuppress set. Strong provenance: 55 committed packages.lock.json + CPM + lockstep Common + transitive MassTransit-v8 pin + auditSources config, and all three CI restores run --locked-mode so committed-lock drift fails the build (D9). The SBOM is a hard gate (43d733f); the license report stays report-only by design. ↑ Implementation 8→9 (2026-07-16 drift-fold, adversarially verified): capability-level comparison found no §32 mechanism ADC's I9 has that Store lacks (identical --locked-mode restores at deploy.yml:42,123,222, identical gating audit/SBOM, byte-identical suppress/lock config); held below 10 by the same transitive-only MassTransit pin caveat as ADC. Evidence: deploy.yml:42,123,222,133-162; Directory.Build.props:8-12,27,39; nuget.config
33 Developer Experience & Inner Loop 2 4 8 8/16 Aspire one-command local stack (SQL/Redis/RabbitMQ/MailDev/Stripe), cross-repo local.props, fast CI slnf, editorconfig parity, local→Azure parity. — CLAUDE.md:13-90; local.props.template; MMCA.Store.CI.slnf
34 Architecture Governance & Docs 2 4 9 8/18 Exemplary automated conformance (23 arch fitness-test classes over shared bases + Store-local guards) + a thorough current CLAUDE.md + ADRs governed canonically in Common (range/count owned by ../MMCA.Common/FACTS.md), completed by the in-repo two-axis scorecard + RemediationBacklog.md (the single per-repo ledger), both committed and kept current per release (the former impl-7 caveat is realized). Evidence: Tests/Architecture/* (StoreArchitectureMap, 23 test classes); CLAUDE.md; committed ArchitectureScorecard.md + RemediationBacklog.md; ADRs in MMCA.Common

Weighted = Maturity·weight / Implementation·weight. Axis-gap finding: §21 Accessibility is honestly M3/I8 (the chromium axe gate earns Implementation 8; Maturity caps at 3 pending a recorded manual screen-reader pass, matching ADC, D6); §13 is the mirror case resolved this cycle (M4/I9 after the pairing gate shipped). The widest remaining M-over-I gap is §20 (M4/I7, residual inline styles).

Indices

  • Maturity index = Σ(maturity×weight) ÷ Σ(weight×4) = 315 ÷ 320 = 98.4% (2026-07-17 re-score: §5 M3→4, §13 M3→4, and §22 M3→4, +2 each at weight 2, 309→315; only §12 and §21 remain at maturity 3)
  • Implementation index = Σ(impl×weight) ÷ Σ(weight×10) = 669 ÷ 800 = 83.6% (2026-07-17 re-score: §5 I7→8, +2 at weight 2, and §27 I8→9, +1 at weight 1, 666→669)
  • Weaker axis: Implementation (execution quality), by ~15 points.
  • No N/A categories: §27 joined the denominators this cycle (ADR-027 superseded ADR-011). §32 weight = 2 (default; raised to 3 only for the published framework MMCA.Common).

Top 5 strengths

  1. Clean Architecture + DDD + CQRS depth, fitness-enforced: §3/§4 (impl 9): inherited framework substance, guarded by the shared NetArchTest suite (23 test classes, StoreArchitectureMap.cs:14-43).
  2. Full microservices parity — §7 (impl 8): database-per-service + per-service outbox + versioned gRPC + MassTransit broker + RS256/JWKS, extractable modules (infra/main.bicep:396-418 per-service DBs, :445-474 Service Bus). (Corrects the prior "single shared DB" assumption.)
  3. Exemplary DevOps / operational floor: §17 (impl 9) / §31 / §12: two-phase Bicep, OIDC + Key Vault MI, passwordless MI-SQL active in prod (2026-07-12), post-deploy smoke gate with auto-rollback (deploy.yml:461-518), cost-guard/dr-drill/load-test workflows.
  4. Reference front-end security — §26 (impl 9): in-memory access token + HttpOnly refresh cookie (no localStorage), hardened origin-pinned CSP, runtime-config fetch (ServerTokenStorageService.cs:10-13, BlazorCspPolicyProvider.cs:71).
  5. Drilled DR + real erasure path (§29 impl 9 / §30 impl 8): recorded 28.9-min restore (DISASTER-RECOVERY.md:146-148), the dr-freshness deploy gate + weekly cron + CI-gated GracefulShutdownTests, IAnonymizable anonymize-in-place + export + residency/PII fitness functions.

Top 5 risks

  1. Accessibility maturity is capped pending a human pass: §21 (mat 3, weight 3): the 22-scan axe suite gates the deploy (impl 8), but the rubric pairs axe-in-CI with a recorded manual screen-reader pass and the ACCESSIBILITY-SCREENREADER-PASS.md results log is still empty (matching ADC's M3).
    • Remediation: record a dated NVDA/VoiceOver pass in the runbook against the running app (needs a human; cannot be done headless). Expected: §21 mat 3→4.
  2. Load evidence is capacity-planning cadence, not a merge gate: §12 (mat 3): the k6 suite is real with pass/fail thresholds and a load-freshness deploy gate, but the test itself runs monthly/on-demand, so a latency regression can merge and deploy inside the freshness window.
    • Remediation: right-size deliberately: either accept the monthly cadence as the recorded posture (matching ADC's accepted §12 M3) or add a cheap latency-regression smoke to the merge path. Expected: decision recorded either way; §12 mat 3→4 only with a gate.
  3. Design-system residual inline styles: §20 (impl 7, the widest M-over-I gap): the token convention is CI-enforced (BrandColorTokenTests), but MudBlazor Style=/CellStyle= parameter values that CSS isolation cannot target without a DOM-changing wrapper remain in cart/catalog markup (ProductList.razor:118).
    • Remediation: move the residual values behind semantic classes via wrapper elements or theme tokens where feasible. Expected: §20 impl 7→8.
  4. Client validation short of server parity: §24 (impl 8): the four create forms' guard/dirty/validated-MudForm markers are CI-gated (FormsConventionTests, M4), but client validation stays MudForm-level rather than full FluentValidation parity with the server rules.
    • Remediation: mirror the remaining server-only rules client-side where they do not need the DB. Expected: §24 impl 8→9.
  5. Concurrency seam residual [C→A]: the ADR-035 client half shipped (11 property-edit requests implement IConcurrencyAware, DTOs expose RowVersion, handlers call SetOriginalRowVersion, stale-token 409 integration-tested), but child-level entities (e.g. ProductVariant under Product) need a child-typed SetOriginalRowVersion overload in MMCA.Common before their edits get the same protection.
    • Remediation: add the aggregate-child overload in Common and sweep (lands in Common, both consumers in one pass). Expected: §8 evidence completes across child edits.

Cross-repo comparison

How Store relates to MMCA.Common (the framework) and MMCA.ADC (the sibling consumer) is maintained once, for all three repos, in ../Docs/Architecture/CrossRepoComparison.md.