Architecture governance
Architecture Evaluation Criteria
A structured rubric for evaluating the architecture of an enterprise application. Each category defines what is being assessed, concrete criteria to check, red flags that signal trouble, and a maturity scale so scores are comparable across reviews.
Although written against a .NET / DDD / Clean Architecture / CQRS stack (modular monolith evolving toward microservices) with a Blazor / MudBlazor front end, the criteria generalize to any enterprise codebase and component-based UI.
The categories are organized in three parts: Part A: Application / Backend Architecture (§1–17), Part B: Front-End / UI Architecture (§18–28), and Part C: Operational, Governance & Cross-Cutting Concerns (§29–34).
Rubric version 2 (2026-09-04). Version 2 keeps 34 categories and every category number, so scorecard rows, backlog items and ADR citations stay stable, and makes two in-place replacements: §10 is now Messaging & Integration Architecture (the former Cross-Cutting Concerns facets are scored in §5, §6, §9, §12, §17 and §29) and §16 is now AI-Native Application Architecture (the former Maintainability & Evolvability criteria live in §32, §33 and §34). It also adds criteria to §4, §7, §8, §9, §11, §12, §14, §17, §29, §33 and §34. Each scorecard's header records the rubric version it was scored against; indices that straddle a version boundary carry a different denominator and are comparable only with that note. Decision record: ADR-110.
How to Use This Rubric
Maturity scale (per category)
Score each category 0–4. Use the same scale everywhere so totals are comparable.
| Level | Name | Meaning |
|---|---|---|
| 0 | Absent | Principle/pattern not applied; actively violated. |
| 1 | Initial | Ad-hoc, inconsistent, present only in isolated spots. |
| 2 | Developing | Applied in most new code; legacy gaps remain; some confusion. |
| 3 | Consistent | Applied uniformly with conventions; enforced by review. |
| 4 | Optimized | Enforced automatically (analyzers/tests/CI), documented, and evolved deliberately. |
Implementation score (per category)
Alongside the maturity level, rate how well each category is actually implemented on a finer 0–10 scale. The two axes measure different things and should both be recorded:
- Maturity (0–4), process: how consistently and how well-governed the pattern is (ad-hoc → enforced by CI).
- Implementation (0–10), substance: how good the implementation is right now, judged against the category's criteria and red flags.
A category can be mature-but-mediocre (enforced conventions wrapped around a weak design) or excellent-but-inconsistent (a strong implementation applied only in spots): two scores capture that difference where one cannot.
| Score | Band | Meaning |
|---|---|---|
| 0 | None | Category not implemented; criteria unmet, red flags pervasive. |
| 1–2 | Poor | Isolated, partial attempts; major gaps; multiple red flags present. |
| 3–4 | Partial | Several criteria met; meaningful gaps remain; some red flags. |
| 5–6 | Adequate | Most criteria met; functional with rough edges; a few red flags. |
| 7–8 | Strong | Nearly all criteria met well; only minor, isolated gaps; no significant red flags. |
| 9–10 | Exemplary | All criteria met to a high standard; reference-quality. 10 = almost perfect: every criterion met at reference quality, no red flags, at most trivial polish left. |
Rule of thumb: the implementation score usually tracks maturity (≈ maturity × 2.5 as a starting point), then nudge it up or down for execution quality the coarse maturity level can't express. A large gap between the two axes is itself a finding worth a note.
Scoring model
- Weight each category by risk to this system (weights below are defaults: adjust per engagement).
- Maturity index = Σ(category maturity score × weight) ÷ Σ(weight × 4) → a 0–100% architecture health index.
- Implementation index = Σ(category implementation score × weight) ÷ Σ(weight × 10) → a parallel 0–100% measure of execution quality. Compare the two indices: a lower implementation index means quality is the weaker axis; a lower maturity index means consistency/governance is.
- Capture evidence (file paths, PRs, ADRs) for every score. A score without evidence is an opinion.
- Re-run quarterly or per-release to trend both indices over time.
Scorecard template
| # | Category | Weight | Maturity (0-4) | Impl (0-10) | Weighted | Evidence / Notes |
|---|-------------------------------|--------|----------------|-------------|----------|------------------|
| 1 | SOLID Principles | 3 | | | | |
| 2 | Design Patterns | 2 | | | | |
| 3 | Clean Architecture | 3 | | | | |
| 4 | Domain-Driven Design | 3 | | | | |
| 5 | Vertical Slice Architecture | 2 | | | | |
| 6 | CQRS & Event-Driven | 2 | | | | |
| 7 | Microservices Readiness | 3 | | | | |
| 8 | Data Architecture | 3 | | | | |
| 9 | API & Contract Design | 2 | | | | |
|10 | Messaging & Integration | 3 | | | | |
|11 | Security | 3 | | | | |
|12 | Performance & Scalability | 2 | | | | |
|13 | Observability & Operability | 2 | | | | |
|14 | Testability & Test Strategy | 3 | | | | |
|15 | Best Practices & Code Quality | 2 | | | | |
|16 | AI-Native Application Arch | 2/NA | | | | |
|17 | DevOps & Deployment | 2 | | | | |
| | **Part B: Front-End / UI**| | | | | |
|18 | UI Architecture & Components | 3 | | | | |
|19 | State Management & Data Flow | 3 | | | | |
|20 | Design System & UI Consistency| 2 | | | | |
|21 | Accessibility (a11y) | 3 | | | | |
|22 | Responsive & Cross-Browser | 2 | | | | |
|23 | Front-End Performance | 2 | | | | |
|24 | Forms, Validation & UX Safety | 2 | | | | |
|25 | Navigation & Information Arch | 2 | | | | |
|26 | Front-End Security | 3 | | | | |
|27 | Internationalization (i18n) | 1 | | | | |
|28 | Front-End Testing & Quality | 3 | | | | |
| | **Part C: Operational & Governance** | | | | | |
|29 | Resilience & Business Continuity| 3 | | | | |
|30 | Compliance, Privacy & Governance| 2 | | | | |
|31 | Cost Efficiency / FinOps | 2 | | | | |
|32 | Dependency & Supply-Chain | 2/3 | | | | |
|33 | Developer Experience & Inner Loop| 2 | | | | |
|34 | Architecture Governance & Docs| 2 | | | | |
§16 is N/A (its weight leaves both denominators) until a product feature calls a language model; §32 is 2 by default and 3 for a published framework.
1. SOLID Principles
Intent: Object/module-level design discipline that keeps code flexible and decoupled.
Criteria
- SRP: each class/handler has one reason to change; no "god" services orchestrating unrelated concerns.
- OCP: behavior extended via new types/strategies/decorators, not by editing switch statements.
- LSP: derived types are substitutable; no
NotSupportedExceptionoverrides or type-sniffing (is/asdispatch). - ISP: interfaces are narrow and role-specific; clients aren't forced to depend on members they ignore.
- DIP: high-level modules depend on abstractions; concretions injected via DI, not
new-ed inline.
Red flags
- Constructors with 8+ dependencies (SRP/ISP smell).
switch/ifon a type enum that grows with every feature (OCP miss).- Domain or application code referencing concrete infrastructure classes (DIP miss).
- Interfaces with a single implementation created only to "mock everything."
Default weight: 3
2. Design Patterns
Intent: Appropriate, idiomatic use of patterns, solving real problems, not pattern theater.
Criteria
- Creational (Factory methods on entities, Builder, Options) used where construction is non-trivial.
- Structural (Adapter, Decorator, Facade) used for boundaries and pipelines: e.g., handler decorator pipeline for validation/logging/transactions.
- Behavioral (Strategy, Mediator/dispatcher, Specification, Observer/domain events) used for variation and decoupling.
- Domain patterns: Result for error flow, Repository/Unit of Work, Specification for query intent, Outbox for reliable messaging.
- Patterns are named consistently and discoverable; team shares vocabulary.
Red flags
- Patterns applied where a plain method would do (Singleton for stateless helpers, abstract factories with one product).
- Anemic "manager/helper/util" classes that hide procedural code.
- Reinvented infrastructure (hand-rolled mediator/retry) where a vetted library exists.
- Exceptions used for control flow where a Result/validation result is the established convention.
Default weight: 2
3. Clean Architecture
Intent: Dependencies point inward; business rules are independent of frameworks, UI, and data stores.
Criteria
- Dependency rule enforced: Domain → (nothing); Application → Domain; Infrastructure/API/UI → inward only. Verified by project references and ideally an architecture test (NetArchTest/ArchUnitNET).
- Domain purity: no EF, ASP.NET, serialization, or framework attributes in the domain layer.
- Ports & adapters: application defines interfaces (ports); infrastructure implements them (adapters).
- Use-case centric: application layer expresses business operations, not CRUD-on-tables.
- Framework concerns (DI wiring, middleware, persistence) live at the outermost ring.
Red flags
- Domain entities decorated with
[Table],[JsonProperty], or referencingDbContext. - Controllers/endpoints calling repositories directly, bypassing the application layer.
- "Shared kernel" turning into a dumping ground that couples everything.
- Circular references between layers or modules.
Default weight: 3
4. Domain-Driven Design
Intent: The model reflects the business; boundaries follow capability boundaries, not technical layers.
Criteria
- Ubiquitous language: type/method names match business terms used by stakeholders.
- Bounded contexts / modules with explicit boundaries and ownership (e.g., Catalog, Sales, Identity).
- Aggregates with clear roots and invariants enforced inside the boundary; references between aggregates by ID, not object graph.
- Value objects for concepts with no identity (Money, Address, EmailAddress); immutability respected.
- Domain events raised by aggregates to signal meaningful state changes.
- Factory methods returning
Result<T>so invalid entities can't be constructed. - Rich behavior on entities, not setters-only.
- Tenancy model stated: an ADR records whether the model is single- or multi-tenant; where multi-tenant, the tenant identifier is a strong type on every tenant-owned aggregate, never a naming convention.
Red flags
- Anemic domain model: all logic in services, entities are property bags.
- Aggregates that load half the database; transactions spanning many aggregates.
- Context boundaries that leak (one module querying another's tables directly).
- Primitive obsession (raw
string/Guid/decimalinstead of strong types and identifier aliases).
Default weight: 3
5. Vertical Slice Architecture
Intent: Code is organized by feature/capability, so a change touches one cohesive slice.
Criteria
- Features grouped by use case (command/query + handler + validator + DTO together), not by horizontal technical folders.
- Each slice is independently understandable and testable; minimal shared mutable state.
- Cross-cutting concerns handled by the pipeline (decorators/behaviors), not duplicated per slice.
- Adding a feature means adding a slice, rarely editing shared switchboards.
- Coupling within a slice is high (cohesive); coupling between slices is low.
Red flags
- "Layered-by-type" sprawl where one feature spreads across
Services/,Repositories/,DTOs/,Validators/with no locality. - Shared base handlers/services that every slice must modify.
- Slices reaching into each other's internals instead of going through a defined contract or event.
Default weight: 2
6. CQRS & Event-Driven Design
Intent: Reads and writes are separated where it pays off; integration via events is reliable.
Criteria
- Commands (mutate, return Result) and queries (read, side-effect-free) are distinct.
- Handler dispatch via a mediator/dispatcher with a decorator pipeline (validation → logging → transaction → handler).
- Read models/projections shaped for the consumer where read/write asymmetry warrants it.
- Outbox pattern for atomic persist-then-publish; no "save then publish and hope."
- Idempotent consumers; events carry enough context; versioning strategy for event schemas.
- Eventual consistency boundaries are explicit and documented (ADR).
Red flags
- "CQRS" that's just two folders sharing one fat model with no real benefit.
- Dual-write to DB and broker without an outbox (lost-message risk).
- Synchronous chains of commands masquerading as events.
- Consumers that aren't idempotent and break on redelivery.
Default weight: 2
7. Microservices Readiness
Intent: Whether services (or future-extractable modules) are independently deployable and own their data.
Criteria
- Service boundaries align with bounded contexts; one team can own and deploy a service.
- Data ownership: each service/module owns its schema; no shared writable database across service boundaries.
- Communication: async events for integration, sync calls only where strong consistency is required; contracts are explicit and versioned.
- Resilience: timeouts, retries with backoff, circuit breakers, bulkheads, graceful degradation.
- Independent deployability: services build/test/deploy independently; backward-compatible contracts.
- Distributed observability: correlation/trace IDs propagate across service hops.
- For a modular monolith: modules implement a common contract, are discovered/registered in dependency order, and are extractable without rewrites.
- Modernization patterns named: an Anti-Corruption Layer wraps any legacy or third-party model at the boundary, and extracting a module or replacing a legacy component follows the Strangler Fig route (new path beside old, traffic moved, old path retired), so a rewrite is never the plan.
Red flags
- "Distributed monolith": services that must deploy together; chatty synchronous call graphs.
- Shared database tables read/written by multiple services.
- No resilience policies; a downstream outage cascades.
- Integration via direct DB access instead of contracts/events.
- A legacy or vendor model leaking into the domain because no translation layer sits at the boundary.
Default weight: 3
8. Data Architecture
Intent: Persistence, consistency, and migrations are deliberate and safe.
Criteria
- Transaction boundaries match aggregate boundaries; unit-of-work scope is clear.
- Migrations are versioned, reviewed, reversible/forward-only by policy, and run in CI/CD.
- Soft-delete + global query filters (or an intentional alternative) applied consistently.
- Audit fields (created/modified by/on) stamped centrally, not per-handler.
- Query efficiency: explicit eager-loading strategy; no N+1; projections for read paths.
- Concurrency handled (optimistic concurrency tokens) where contention exists.
- Per-service data isolation where microservices are in play (see §7).
- Zero-downtime schema change: migrations follow expand-contract, so the running version and the new schema coexist through a deploy; a contract step (drop/rename) ships as a separate release.
- Analytical reads separated from OLTP: reporting, export and analytics run off CDC, a replica, or an export pipeline, or a documented decision states that none exist; the transactional store is never the BI source of record.
- Polyglot persistence by decision: any second engine (document, cache, search, vector) is chosen by ADR with its consistency model stated, not adopted ad hoc.
- Tenant isolation model (database, schema, or row with a global filter) is decided by ADR, or single tenancy is stated.
Red flags
- Migrations hand-applied to production; schema drift between environments.
- N+1 queries,
Includechains loading whole graphs, client-side evaluation. - Cross-module/service joins coupling independent contexts.
- Hard deletes where soft-delete is the convention (orphaned references, lost audit trail).
- A migration that drops or renames in the same release that stops writing the old shape (no expand-contract).
- Reports and exports running heavy reads against the transactional database with no separation.
Default weight: 3
9. API & Contract Design
Intent: External and inter-service contracts are clear, stable, and evolvable.
Criteria
- Consistent resource/endpoint design (REST/minimal APIs/gRPC) with predictable shapes.
- Versioning strategy for breaking changes; backward compatibility honored.
- Standardized error responses (e.g., Problem Details) and consistent status codes.
- Request validation at the edge; DTOs decoupled from domain entities (manual mapping or mapper by ADR).
- Pagination, filtering, sorting conventions are uniform.
- Contracts documented (OpenAPI) and generated/verified, not hand-maintained drift.
- Contract tests at the boundary: an OpenAPI baseline diff or consumer-driven contract tests fail the producer's build on a breaking change; async contracts (integration events) are documented alongside OpenAPI (AsyncAPI or equivalent).
Red flags
- Domain entities serialized directly to the wire (leaks internals, couples API to schema).
- Breaking changes shipped without versioning; consumers break silently.
- Inconsistent error shapes; 200-with-error-body anti-pattern.
- Undocumented or stale API specs.
- A breaking change that only a consumer's production failure could have caught.
Default weight: 2
10. Messaging & Integration Architecture
Intent: Integration over a broker or an edge gateway is reliable, observable and evolvable: the transport, the failure paths and the long-running processes are deliberate. (§6 scores the in-process command/query pipeline and the outbox write side; this scores what happens once a message leaves the process. Replaced Cross-Cutting Concerns in rubric v2; those facets are scored in §5, §6, §9, §12, §17 and §29.)
Criteria
- Broker topology decided by ADR: transport choice (Service Bus, RabbitMQ, Kafka, in-process), topics vs queues, one publishing boundary per source, and the local/prod parity of that choice (see §33).
- Delivery semantics stated per consumer: at-least-once with idempotent consumers (inbox or idempotency key), ordering expectations declared, duplicate handling tested.
- Dead-letter and poison-message handling: retries with backoff and a bounded attempt count, then a dead-letter path with an operational procedure (alert, inspect, replay or discard).
- Retention and replay: message and outbox retention are bounded and documented; a consumer can be replayed or rebuilt after an outage.
- Contract evolution: integration events are versioned; changes are additive unless a new version is published; consumer-driven contract tests (or an equivalent schema gate) catch a breaking producer change before deploy.
- Long-running processes: work spanning aggregates or services runs as a saga or process manager with explicit compensation, never as a distributed transaction; the state is persisted and timeouts are defined.
- Edge integration: the API gateway / BFF layer routes, aggregates and authenticates without becoming a second business layer; service discovery is configuration, not hard-coded hosts.
Red flags
- No dead-letter story: a poison message blocks the queue or is silently dropped.
- Retries without idempotency (duplicate side effects) or without backoff (retry storms).
- An integration event whose shape changed with no version and no consumer check.
- A multi-aggregate workflow held open in one database transaction, or compensation that exists only in prose.
- Gateway code that carries business rules, or a consumer reading a producer's tables directly.
Default weight: 3
11. Security
Intent: AuthN/AuthZ, secrets, and data protection are correct by construction.
Criteria
- Authentication centralized; tokens validated; identity flows documented (e.g., dual-fetch ADR).
- Authorization enforced at the right layer (policy/resource-based), not just UI hiding.
- Secrets in a vault/managed identity, never in source or plain config; rotation possible.
- Input validation and output encoding guard against injection/XSS; parameterized queries only.
- Transport security (TLS), data-at-rest protection, PII handling, and least-privilege access.
- Dependency and package vulnerability scanning in CI (audit sources configured).
- OWASP Top 10 reviewed; rate limiting / anti-automation where exposed.
- Threat model: a written threat model exists, is revisited when a trust boundary changes, and its mitigations trace to code or config.
- Service-to-service authentication: internal calls (HTTP, gRPC, broker) are authenticated and authorized by identity, never trusted by network position alone; private endpoints and VNets are defense in depth, not the control.
- Tenant-scoped authorization where multi-tenant: every query and command carries the tenant scope and a fitness function proves it; single tenancy is stated by ADR.
Red flags
- Authorization checks only in the UI; APIs callable unguarded.
- Connection strings/keys committed or in appsettings.
- Over-broad permissions (admin everywhere), no least privilege.
- Ignored package audit warnings.
- Internal services that trust any caller on the network; no threat model, or one nobody has reopened since the first release.
Default weight: 3
12. Performance & Scalability
Intent: The system meets latency/throughput goals and scales horizontally.
Criteria
- Async I/O throughout; no sync-over-async; no blocking the request thread.
- Hot-path query efficiency (projections, indexes, no N+1); measured, not assumed.
- Caching at appropriate tiers with sound invalidation.
- Stateless services enabling horizontal scale; session/state externalized.
- Load/stress tested against realistic volumes; capacity provisioning evidence-based (right-sized, not guessed).
- Pagination/streaming for large result sets; backpressure on queues.
Red flags
.Result/.Wait()deadlock risks; synchronous DB calls in async pipelines.- Unbounded queries returning whole tables.
- In-memory state preventing scale-out.
- Provisioning by guesswork (massive over- or under-provisioning) with no load data.
- A cache with no invalidation story (stale data), or caching used to mask a slow query.
Default weight: 2
13. Observability & Operability
Intent: You can understand and operate the system in production.
Criteria
- Structured logging with correlation/trace IDs flowing across module/service boundaries.
- Distributed tracing (OpenTelemetry) and metrics (RED/USE) wired to a backend (e.g., App Insights).
- Health checks (liveness/readiness) and dependency checks exposed for orchestrators.
- Alerting on SLO breaches; dashboards exist and are used.
- Noise control: high-volume/low-value telemetry (e.g., poll spans) deliberately filtered to manage cost.
- Runbooks for common failures; graceful shutdown and startup ordering.
Red flags
Console.WriteLine/unstructured logs; no correlation across services.- No health endpoints; orchestrator can't tell if the app is alive.
- Telemetry cost unmanaged (everything logged at Information) or, conversely, nothing logged.
- No alerts: failures discovered by users.
Default weight: 2
14. Testability & Test Strategy
Intent: The design supports fast, reliable, meaningful tests at the right levels.
Criteria
- Healthy test pyramid: many fast unit tests on domain/application, fewer integration, few E2E.
- Domain logic testable without infrastructure (pure, injectable dependencies).
- Integration tests cover persistence, messaging, and module wiring against real-ish dependencies (Testcontainers/Aspire).
- Shared test infrastructure (page objects, fixtures, base classes) reused across consumers, not duplicated.
- Architecture tests enforce dependency rules automatically.
- Tests are deterministic, isolated, and run in CI as a gate.
- Coverage tracked on meaningful paths (not chased as a vanity number).
- Contract tests sit at the integration tier: API baseline or consumer-driven contract checks and integration-event schema checks run in CI (see §9, §10).
Red flags
- Inverted pyramid (mostly slow E2E), flaky tests, tests disabled/skipped without tracking.
- Logic only reachable through HTTP, forcing heavy integration tests for unit-level concerns.
- Mock-everything tests that assert implementation details, not behavior.
Default weight: 3
15. Best Practices & Code Quality
Intent: Day-to-day craftsmanship that keeps the codebase healthy.
Criteria
- Analyzers at error severity (style, security, threading, maintainability) enforced in CI; warnings-as-errors.
- Consistent conventions (file-scoped namespaces, naming, immutability, nullability) applied uniformly.
- Central package management; pinned, audited dependencies; intentional version policy (e.g., a library pinned for licensing reasons).
- Error handling via the established pattern (Result vs. exceptions) used consistently.
- Code is self-documenting; comments explain why, not what; ADRs capture significant decisions.
- Dead code, TODOs, and suppressions are tracked, not accumulated.
Red flags
- Disabled analyzers / blanket
#pragma warning disable. - Mixed conventions, inconsistent error handling.
- Unpinned or unaudited dependencies; accidental major-version bumps reintroducing known-bad packages.
Default weight: 2
16. AI-Native Application Architecture
Intent: Where a product feature calls a language model or an agent, that dependency is governed like any other external system: isolated, versioned, evaluated, observed and bounded in what it may do. (Developer-side AI tooling is scored in §33; this category is about the product. Replaced Maintainability & Evolvability in rubric v2; those criteria live in §32, §33 and §34.)
Applicability: mark N/A (excluded from both denominators) when no product feature calls a model, and say so in the scorecard's N/A note so the scope-out is explicit rather than a silent zero. Score it as soon as a single feature does.
Criteria
- Model calls behind a port: the application layer depends on an interface; the provider SDK, model routing / AI gateway and prompt assembly live in infrastructure, so a provider or model swap is an adapter change.
- Prompt and model versioning: prompts are source-controlled artifacts with a version; the model identifier is pinned per environment and a change is a reviewed change, not config drift.
- Evaluation gates CI: a repeatable evaluation suite (golden cases, rubric or judge scoring, regression thresholds) runs before a prompt, model or retrieval change ships.
- Guardrails at the boundary: input and output validation, PII redaction before the call and in the trace, injection defenses for retrieved or user content, and a content policy the feature cannot bypass.
- Least-privilege tool calling: an agent's tools are explicit, authorized per caller, and idempotent or confirmable; consequential actions require a human in the loop or an explicit policy that says they do not.
- Retrieval as data architecture: vector or hybrid search stores follow §8 (ownership, retention, per-service isolation) and §30 (PII, erasure); embeddings are refreshed when the source changes.
- LLM observability and cost: token usage, latency, model id and prompt version are on every trace; per-feature cost is attributable (§31) and alerts exist for runaway spend.
Red flags
- Provider SDK types or prompt strings inside domain or application code.
- A prompt or model change shipped with no evaluation run.
- An agent that can call any tool with the caller's full permissions, or that takes an irreversible action with no confirmation path.
- User or retrieved content concatenated into a prompt with no injection handling; PII in the prompt log.
- No idea what a feature costs per call.
Default weight: 2 (N/A until a product feature calls a model)
17. DevOps & Deployment
Intent: Building, releasing, and provisioning are automated, repeatable, and safe. (The local developer experience / inner loop behind this (local orchestration, cross-repo dev, build speed) is scored separately in §33.)
Criteria
- CI gates: build, analyzers, tests, security/audit on every PR; fast feedback.
- CD with repeatable, automated deployments; rollback strategy.
- Infrastructure as Code (Bicep/Terraform): environments reproducible; no click-ops drift.
- Secrets/identity via managed identity / OIDC, least privilege for deployment principals.
- Environment parity; configuration externalized per environment.
- Configuration via strongly-typed options validated at startup; environment overrides are clean and no secret sits in a config file.
- Progressive delivery: release is decoupled from deploy via feature flags, slots (blue/green) or canary, or single-slot deploy risk is explicitly accepted in an ADR with a rehearsed rollback; branches are short-lived and trunk-based, gated by CI.
- Cost awareness: provisioning right-sized with evidence; temporary scale-ups tracked with revert plans.
- Containerization/orchestration (or Aspire-style local-to-cloud parity) where applicable.
Red flags
- Manual deploys; environment drift; "works on the build server."
- Infra changed by hand in the portal; no IaC source of truth.
- Long-lived elevated credentials; secrets in pipelines.
- Scale-ups left running after the event that needed them.
- Magic strings for config; secrets in config files.
- A merge to main that is also a production deploy with no flag, slot or rehearsed rollback.
Default weight: 2
Part B: Front-End / UI Architecture
Categories §18–28 assess the presentation tier. They use the same 0–4 maturity scale and weighting model as Part A. Where a concern has a backend counterpart (security, performance, testing), Part B focuses on the client/UI-specific facets and cross-references Part A.
18. UI Architecture & Component Design
Intent: Components are cohesive, reusable, and composed cleanly, the UI has a deliberate structure, not page-sized blobs.
Criteria
- Container/presentational split: smart components own data/behavior; dumb components render from parameters and raise events. Logic isn't buried in markup.
- Component contracts: typed parameters with sensible defaults; outputs via callbacks/
EventCallback; two-way binding used intentionally, not everywhere. - Composition over inheritance: layouts, render fragments, and slots compose UI; minimal deep component hierarchies.
- Reuse: shared/primitive components live in a common UI library (consumed by multiple apps), not copy-pasted per page.
- Render lifecycle discipline: expensive work kept out of render;
ShouldRender/keys/@keyused to control re-render where it matters. - Separation of concerns: no direct data-access or business rules in components; they call application services/clients.
Red flags
- 1000-line page components mixing data fetching, validation, and markup.
- Business logic in
.razorcode-behind that belongs in the application layer. - Prop-drilling many levels deep instead of composition or scoped state.
- Components that reach into global singletons for data instead of receiving it.
Default weight: 3
19. State Management & Data Flow
Intent: Client state has a clear owner and predictable flow; server state is cached and invalidated deliberately.
Criteria
- Single source of truth per piece of state; ownership is explicit (component-local vs. scoped service vs. global store).
- Unidirectional data flow: state flows down via parameters, changes flow up via events; avoid hidden mutation of shared objects.
- Server vs. client state distinguished: fetched data cached with a staleness/invalidation strategy; not refetched on every render.
- Component communication via well-defined channels (cascading values, scoped state services, mediators): not static mutable globals.
- Render correctness:
StateHasChangedcalled intentionally; async state updates marshalled to the UI thread/context correctly. - Lifetime correctness: scoped vs. singleton services chosen correctly for the hosting model (Server vs. WASM); no accidental cross-user state leakage.
Red flags
- Static mutable fields holding user/session state (leaks across users in Blazor Server).
- Stale UI because a parent mutated state but didn't notify children (e.g., wrapped guard components reading a stale
IsDirtybecause the parent didn'tStateHasChanged()before navigating). - Refetching/recomputing on every render; no memoization or caching.
- "Spooky action at a distance": multiple components mutating one shared object.
Default weight: 3
20. Design System, Theming & UI Consistency
Intent: A coherent visual language enforced by a component library, not re-implemented per screen.
Criteria
- Component library used consistently (e.g., MudBlazor): teams build on it rather than bypassing it with raw HTML/CSS.
- Design tokens / theme centralized (palette, typography, spacing, breakpoints); dark/light or brand variants driven from the theme.
- Consistency: spacing, density, iconography, button hierarchy, empty/loading states look the same across pages.
- Encapsulated overrides: custom styling wrapped in reusable components, not scattered inline styles or
!importantoverrides. - Known-issue guardrails: wrappers/conventions exist around library quirks so every page doesn't re-hit the same bug (e.g., a grid wrapper that normalizes paging/sorting behavior).
Red flags
- Mixed component libraries or hand-rolled controls duplicating library ones.
- Inline styles and magic pixel values everywhere; no shared tokens.
- Inconsistent loading/empty/error treatments per page.
- Library defaults fought page-by-page instead of fixed once in a shared wrapper.
Default weight: 2
21. Accessibility (a11y)
Intent: The UI is usable by everyone, including assistive-technology users, and ideally enforced, not aspirational.
Criteria
- Semantic structure: correct landmarks/headings/lists; interactive elements are real buttons/links, not click-handlers on
divs. - Keyboard operability: everything reachable and operable by keyboard; logical tab order; visible focus; no keyboard traps.
- ARIA where needed: names/roles/states on custom widgets; live regions for async updates; relies on native semantics first.
- Color & contrast: meets WCAG 2.1 AA contrast; information not conveyed by color alone.
- Forms: labels associated with inputs; errors announced and programmatically linked to fields.
- Verification: automated checks (axe/Lighthouse) in CI plus periodic manual screen-reader/keyboard passes; target conformance level stated (e.g., WCAG 2.1 AA).
Red flags
div/spanclick handlers with no role/keyboard support.- Placeholder-as-label; unlabeled icon buttons; missing alt text.
- Focus lost or invisible after navigation/dialog open.
- No a11y testing in CI; accessibility treated as a post-launch fix.
Default weight: 3
22. Responsive Design & Cross-Browser/Device
Intent: The UI works across viewport sizes, input modes, and supported browsers.
Criteria
- Fluid/responsive layouts via the design system's grid/breakpoints; no fixed-width desktop-only screens.
- Touch and pointer both supported; adequate target sizes; no hover-only affordances for critical actions.
- Supported matrix defined (browsers/devices) and verified; graceful degradation outside it.
- Content reflow: tables/grids/dialogs adapt or provide mobile alternatives; no horizontal scrolling of core content.
- Density options where data-dense (comfortable/compact) without breaking layout.
Red flags
- Pixel-perfect desktop layouts that break below a breakpoint.
- Data grids unusable on mobile with no alternative view.
- Hover-only menus/tooltips hiding essential actions on touch.
- "Works in Chrome" with no other-browser verification.
Default weight: 2
23. Front-End Performance & Rendering
Intent: The UI loads and responds fast; rendering work is bounded. (Complements §12: this is the client side.)
Criteria
- Initial load: bundle/payload size controlled; lazy-loading/code-splitting for heavy routes; prerender/SSR where it helps perceived speed.
- Render efficiency: avoid unnecessary re-renders (
ShouldRender,@key, stable callbacks); virtualization for long lists/grids. - Data efficiency: server-side paging/filtering/sorting for large sets (not loading everything client-side); debounced inputs.
- Perceived performance: skeletons/optimistic UI/loading states; interactions stay responsive (no blocking the UI thread/circuit).
- Asset hygiene: images sized/compressed; fonts/icons subset; caching headers set.
- Measured: Core Web Vitals (LCP/INP/CLS) or equivalent tracked, not assumed.
Red flags
- Loading entire datasets into the client then paging in memory.
- Re-rendering large trees on every keystroke; janky typing.
- No virtualization on long grids/lists.
- Blocking the Blazor Server circuit / UI thread with sync work.
Default weight: 2
24. Forms, Validation & UX Safety
Intent: Data entry is safe, forgiving, and consistent, users don't lose work or get confused by errors.
Criteria
- Validation parity: client-side validation for fast feedback mirrors server-side rules (server remains authoritative; see §11).
- Error presentation: field-level, human-readable, tied to inputs; summary for form-level errors; consistent styling.
- Dirty tracking & unsaved-changes guards: navigating away from an edited form prompts; guard reads current dirty state reliably.
- States covered: loading, submitting (disabled/!double-submit), success, empty, and error states all designed.
- Forgiving input: sensible defaults, input masks/formatters, undo where feasible; destructive actions confirmed.
- Accessibility of validation: errors announced and associated with fields (ties to §21).
Red flags
- Client validation that disagrees with the server (false pass/fail).
- Silent data loss on navigation; or a guard that misfires because it reads stale dirty state.
- Double-submit allowed; no disabled/spinner state on submit.
- Generic "an error occurred" with no field context.
Default weight: 2
25. Navigation, Routing & Information Architecture
Intent: Users can find their way; routes are meaningful, guarded, and role-aware.
Criteria
- Route design: clean, bookmarkable, deep-linkable URLs; parameters typed and validated.
- Guards & authorization: route-level auth enforced (UI hiding is not security; see §11); unauthorized access redirects sensibly.
- Role-based flows: navigation reflects actor roles; flows documented (e.g., per-actor navigation diagrams) and match the implementation.
- Wayfinding: breadcrumbs/active states/back behavior consistent; not-found and forbidden pages handled.
- State on navigation: query/route state preserved appropriately; in-progress work protected (ties to §24).
Red flags
- Menu items visible to roles that can't use them (or hidden items still routable).
- Non-bookmarkable state hidden entirely in memory.
- Navigation flows that drift from the documented per-role design.
- No handling for 404/403 within the app shell.
Default weight: 2
26. Front-End Security
Intent: The client doesn't become the weak link, XSS, token handling, and trust boundaries are correct. (Complements §11.)
Criteria
- Output encoding / XSS: no unsanitized HTML injection (
MarkupString/innerHTMLonly on trusted, sanitized content); user content encoded by default. - Token & session handling: auth tokens stored and transmitted safely; minimal sensitive data in the browser; logout clears state.
- Content Security Policy and security headers configured; third-party scripts vetted.
- No secrets in the client: API keys/secrets never shipped to WASM/browser bundles.
- Client is untrusted: all authorization re-checked server-side; client checks are UX only.
- Dependency hygiene: JS/interop and front-end packages audited for vulnerabilities.
Red flags
- Rendering user input as raw HTML/markup without sanitization.
- Secrets or privileged config embedded in client bundles.
- Auth tokens in insecure storage or logged.
- Authorization enforced only in the UI.
Default weight: 3
27. Internationalization & Localization
Intent: The UI can be translated and respects culture, if in scope. (Score weight 0–1 if single-locale by design.)
Criteria
- Externalized strings: UI text in resource files, not hard-coded; keys consistent.
- Culture-aware formatting: dates, numbers, currency, time zones formatted per culture, not hard-coded.
- Layout tolerance: components handle text expansion and, where required, RTL.
- Locale selection: discoverable, persisted; server and client cultures aligned.
- Pluralization & interpolation handled by the i18n mechanism, not string concatenation.
Red flags
- Hard-coded user-facing strings throughout markup.
- Manual date/number formatting ignoring culture.
- Layouts that break with longer translations.
- Concatenated sentences impossible to translate correctly.
Default weight: 1
28. Front-End Testing & Quality
Intent: The UI is verified at the right levels with stable, meaningful tests. (Complements §14.)
Criteria
- Component tests (e.g., bUnit) for rendering logic, parameters, events, and conditional UI.
- End-to-end tests (e.g., Playwright) for critical user journeys; shared E2E infrastructure (page objects, fixtures, abstract bases) reused across apps rather than duplicated.
- Accessibility checks (axe/Lighthouse) and ideally visual-regression tests run in CI.
- Stability: tests use robust selectors (roles/test-ids), avoid timing flakiness, run as a merge gate.
- Coverage of states: loading/empty/error/edge states tested, not just the happy path.
- Right pyramid: many fast component tests, fewer broad E2E flows.
Red flags
- Only manual click-testing; no automated UI tests.
- Flaky, sleep-based E2E using brittle CSS-path selectors.
- Duplicated E2E setup per app instead of a shared package.
- Happy-path-only coverage; a11y untested.
Default weight: 3
Part C: Operational, Governance & Cross-Cutting Concerns
Categories §29–34 span backend and front end and are judged over the system's full lifecycle. They use the same 0–4 maturity scale and weighting.
29. Resilience, Reliability & Business Continuity
Intent: The system survives partial failure and recovers from disaster within defined objectives. (Extends the resilience facets of §7/§12 into a first-class recovery story.)
Criteria
- Failure isolation: timeouts, retries with backoff + jitter, circuit breakers, bulkheads; one failing dependency doesn't cascade.
- Graceful degradation: fallbacks/queued work when a dependency is down; the broker buffers integration events and the outbox guarantees eventual delivery after recovery.
- Backup & restore: automated backups for every stateful store (per-service DBs); restores actually tested; documented procedure.
- Disaster recovery: defined RTO/RPO per service; failover/multi-region strategy, or a conscious and documented acceptance of single-region risk.
- Reliability targets: SLOs/error budgets defined and measured; health/readiness probes drive orchestration and auto-heal.
- Failure testing: chaos/fault injection or at least documented failure-mode analysis; startup ordering and graceful shutdown verified.
- One resilience mechanism: timeout/retry/circuit-breaker policies are applied through a shared pipeline (decorators, named Polly pipelines), never re-implemented per call site.
Red flags
- Backups that have never been restored (untested recovery).
- Undefined RTO/RPO; "we'd figure it out."
- Retries without backoff/idempotency (retry storms, duplicate side effects).
- Single points of failure with no failover and no explicit, documented risk acceptance.
- Inconsistent, per-call retry logic.
Default weight: 3
30. Compliance, Privacy & Data Governance
Intent: Personal and regulated data is classified, governed, and handled lawfully across its lifecycle. (§11 defends against attackers; this answers to regulators.)
Criteria
- PII/sensitive-data inventory: what's collected, where it's stored, who can access it.
- Retention & purge: defined retention periods with an actual purge mechanism; reconcile soft-delete with right-to-erasure: soft-delete preserves rows, erasure requires real removal/anonymization, so a hard-delete/anonymize path must exist for subject requests.
- Data-subject rights: export/access and deletion requests supported operationally, not just in theory.
- Data residency & sovereignty: storage region matches regulatory/contractual requirements.
- Consent & lawful basis: captured where required and auditable.
- Audit trail: audit fields + access logs support accountability and legal hold.
Red flags
- Soft-delete as the only deletion path, with no erasure mechanism (direct GDPR/CCPA conflict).
- No retention policy: data kept forever by default.
- PII in logs/telemetry; PII replicated into read models/caches without governance.
- Residency requirements unverified for the chosen region.
Default weight: 2
31. Cost Efficiency / FinOps
Intent: Cloud spend is proportional to value and driven by data, not guesswork. (§17 mentions cost; this makes it a first-class axis.)
Criteria
- Right-sizing: compute/database tiers matched to measured load; scaling rules backed by real traffic, not worst-case guesses.
- Reversible scale events: temporary scale-ups for known peaks have an automated or scheduled revert; nothing stays scaled up after the event.
- Telemetry/log cost control: retention tuned, high-volume/low-value signals filtered, sampling where appropriate (logs and traces are a real line item).
- Resource lifecycle: dev/test/ephemeral resources deprovisioned; orphaned resources reaped.
- Cost visibility: spend attributable per service/environment; budgets/alerts; cost considered in design (poll intervals, chatty calls).
- Tier fit: shared/serverless/Basic tiers used for intermittent or archival workloads.
Red flags
- Provisioning by guesswork: large over- (or under-) provisioning with no load evidence.
- Scale-ups left running after the event that justified them.
- Unbounded log/trace retention; everything emitted at Information.
- No per-service/environment cost attribution; surprises on the bill.
Default weight: 2
32. Dependency & Supply-Chain Management
Intent: Third-party and inter-package dependencies are controlled, auditable, and evolve safely, especially critical for a framework that publishes packages. (Elevates §15's hygiene into release + provenance.)
Criteria
- Pinned, central versions (central package management); deliberate, reviewed upgrades: no accidental major bumps reintroducing known-bad versions (a library pinned for licensing must stay pinned).
- Vulnerability auditing in CI across all feeds: including private/GitHub Packages, with audit-source config so private feeds don't break the build.
- Published-package versioning: semantic versioning + a breaking-change policy for the framework's packages, so consumers know what an upgrade implies.
- Coordinated rollout: framework changes swept across all consumers together; no long-lived divergent versions or lingering partial rollouts.
- Provenance & integrity: SBOM, lock files, trusted sources only; transitive dependencies reviewed.
- License compliance: dependency licenses tracked; commercial-license constraints honored.
Red flags
- Blanket "update all packages" that reintroduces a pinned/known-bad dependency.
- Private-feed audit warnings ignored or suppressed wholesale.
- Consumers stranded on divergent framework versions.
- No SBOM/lock files; unaudited transitive dependencies; unreviewed licenses.
Default weight: 2 (raise to 3 for a published framework such as MMCA.Common)
33. Developer Experience & Inner Loop
Intent: Developers build, run, test, and iterate locally with fast, low-friction feedback. (Promoted out of §17: that scores release/ops automation; this scores the inner loop.)
Criteria
- Fast inner loop: build and test times kept low; incremental builds; hot reload where available; tests fast enough to run constantly.
- One-command local run: local orchestration brings up the whole system (e.g., Aspire AppHost spins up services, dependencies, and dashboards) with no manual wiring.
- Cross-repo local dev: a frictionless way to develop the shared framework alongside consumers without publishing (e.g., a
local.propsoverride pointing at../MMCA.Common/Source); documented and gitignored. - Onboarding: a new dev is productive quickly; prerequisites, secrets bootstrap, and run instructions documented and current.
- Consistent tooling: analyzers/formatting/
.editorconfigenforce the same rules locally as CI; fast pre-merge feedback. - Local/cloud parity: local topology mirrors production (Aspire-to-Azure) so integration bugs surface locally, not in prod.
- Discoverable commands: build/test/migrate/run are each one obvious command.
- AI-assisted engineering guardrails: coding agents and workflow scripts run behind enforced hooks (denied destructive commands, deploy confirmation, PR-only landing), their output passes the same CI gates as a human change, and the rules are versioned in the repo.
Red flags
- Multi-step manual setup to run locally; tribal knowledge required.
- Slow builds/tests that discourage running them.
- No cross-repo dev path: must publish a package to test a framework change.
- Local environment diverges from prod (different brokers/DBs) hiding integration bugs.
- Agentic tooling that can push, deploy or delete with no hook or gate between it and production.
Default weight: 2
34. Architecture Governance & Documentation
Intent: Decisions are recorded, conformance is enforced, and the system is documented so it stays coherent as it evolves. (Absorbs the former §16 Maintainability & Evolvability in rubric v2: the evolvability property and the machinery that protects it are scored together here; upgrade strategy lives in §32 and onboarding cost in §33.)
Criteria
- Decision records: significant decisions captured as ADRs with context/rationale/consequences, and kept current (e.g., the
ADRs/set on manual mapping, navigation populators, outbox dual-dispatch, auth dual-fetch). - Fitness functions: architecture rules enforced automatically as executable governance (architecture tests / NetArchTest), not just prose.
- Living documentation: an architecture map/overview (C4, or a dependency-ordered class encyclopedia) that matches the code and is maintained.
- Documented conventions: contributor guides (
CLAUDE.md) describe patterns, layering rules, and how to extend; discoverable and current. - Change governance: a lightweight process for evolving cross-cutting patterns; consistency enforced across modules and repos.
- Traceability: docs link to code/ADRs; stale docs are detected and pruned.
- Coupling measured and enforced: low coupling / high cohesion checked by dependency-graph or change-coupling fitness functions; modules swap independently.
- Tech-debt register: one ledger derived from the scorecard, serviced each cycle, with conscious deferrals recorded rather than silently left low.
Red flags
- Significant decisions live only in people's heads; ADRs absent or stale.
- Architecture rules exist only as prose nobody checks (no fitness functions).
- Diagrams/docs that contradict the code (worse than none).
- No documented conventions; every module reinvents patterns.
- Shotgun surgery: one change touches many modules.
Default weight: 2
Appendix: Quick-Scan Checklist
A 2-minute triage before the full evaluation: any "no" warrants a deeper look.
- Can you draw the dependency graph and is it acyclic and inward-pointing?
- Is the domain layer free of framework references?
- Does each service/module own its own data?
- Is there an outbox (or equivalent) guaranteeing persist-then-publish atomicity?
- Are authorization checks enforced server-side at the application boundary?
- Are secrets out of source and config?
- Do analyzers + tests gate every merge?
- Are migrations automated and reviewed?
- Do traces/correlation IDs span service hops?
- Is provisioning sized from load data rather than guesswork?
- Are ADRs present for the non-obvious decisions?
- Can a new feature be added as one cohesive slice?
- Is there a dead-letter path with an operational procedure, and is every multi-step workflow a saga with compensation?
- Is there a threat model, and are internal service calls authenticated by identity rather than network position?
- Can a release be switched on or rolled back without a redeploy (flags, slots), or is that risk explicitly accepted?
- If a product feature calls a model, is the call behind a port with an evaluation suite gating CI?
Front-end / UI:
- Is UI logic out of components and in the application layer (components stay presentational)?
- Does each piece of client state have one clear owner, with no static mutable cross-user state?
- Is the component library/design system used consistently instead of bypassed page-by-page?
- Are interactive elements real controls (keyboard-operable, labeled), with a stated WCAG target?
- Do large grids/lists use server-side paging and virtualization rather than loading everything client-side?
- Do edit forms guard against unsaved-changes loss with reliable dirty tracking?
- Are routes guarded server-side (UI hiding is not authorization) and role flows matching the docs?
- Is user content encoded/sanitized, with no secrets in client bundles?
- Are there automated component + E2E tests (with shared E2E infra) gating merges?
Operational / governance:
- Has a database restore actually been tested, with defined RTO/RPO per service?
- Do retries use backoff + idempotency, and does the broker/outbox guarantee delivery after recovery?
- Is there a real erasure/anonymize path reconciling soft-delete with right-to-be-forgotten?
- Is data residency verified for the region, and is PII kept out of logs/telemetry?
- Is provisioning sized from load data, with temporary scale-ups auto-reverted?
- Are log/trace retention and volume tuned for cost?
- Are package versions pinned/audited, with coordinated cross-consumer upgrades and licenses honored?
- Can the whole system be run locally with one command, and the framework developed against consumers without publishing?
- Are architecture rules enforced by fitness functions (tests), and are ADRs + the architecture map current and matching the code?