
Evincia
eShopOnWeb — Modernization Readiness Profile
A readiness profile produced with the Evincia diagnostic engine against a public codebase. eShopOnWeb is Microsoft's clean-architecture ASP.NET Core reference application -- the modern sibling of the deliberately-legacy eShopModernizing samples. Read beside the eShop LegacyWebForms profile (32/100, Red), this is the control pair: the same product domain, two platform eras, read by the same instrument at full coverage. A gauge that only ever reads Red is not a gauge -- and neither is one that reads Green on cue. This is what the needle does on a healthy system: 77 out of 100, a full band above the legacy cohort's best and, scored honestly, three points short of Green, with every missing point named below.
What leaders should take away
- A modern stack reads clean mechanically: zero platform-obsolescence findings.
- Scored 77/100, high Yellow -- three points short of Green, and every missing point is named.
- The remaining work is hygiene and posture -- package currency, an HTTP seam, a cache decision -- not rewrites.
- The measured test net covers the healthy core, not the code the one real refactor touches.
- The engine routed its two ambiguous findings to the architect instead of asserting them.
Check it yourself
Nothing here rests on trust alone. Clone the repository at the pinned commit and check every cited finding against the source. The engine that produced these findings is Evincia's and the run is deterministic -- pinned to the engine commit below -- so the output is fixed and the evidence is inspectable, even though re-running the engine end to end is ours to do. The 0–100 score on top of the findings is senior architect judgment, as the score section below spells out.
| Repository | github.com/MicrosoftLearning/eShopOnWeb · the Microsoft Learning home of the app; fork lineage from dotnet-architecture/eShopOnWeb (archived upstream 2025-01-13) |
|---|---|
| Pinned commit | ffc8786 · last commit 2026-01-27 |
| License | MIT · attribution preserved: "Copyright (c) .NET Foundation and Contributors" |
| Engine commit | 8fbc30e · Roslyn-based; nine automated categories (1–9), three senior architect review categories (10–12) |
| Coverage | Full semantic coverage -- 10 of 10 projects loaded with semantic models, no syntax-only fallback, no skips (confidence impact: None) |
Inspect the evidence: git clone the repo and git checkout ffc8786, then open the files each finding cites and read them against the source. (Re-running the diagnostic end to end points the Evincia engine, commit 8fbc30e, at eShopOnWeb.sln in engagement mode with full coverage enforced -- the engine is ours, so that step stays in-house.) All ten projects are SDK-style .NET 8; the repo's global.json pins an 8.0.x SDK. Findings paraphrase the source and cite file/project locations; no substantial verbatim code is reproduced.
Executive summary
eShopOnWeb is Microsoft's clean-architecture e-commerce reference application on ASP.NET Core: ten SDK-style projects, all on .NET 8 (LTS, in support) -- a domain core, an infrastructure layer, an MVC web host, a REST API, a Blazor WebAssembly admin, the shared contracts library behind it, and four test projects. The architectural intent is legible from the dependency graph alone: the core has one external connection, there are no circular references, and the Ardalis suite, MediatR, FluentValidation, and AutoMapper appear together as a deliberate school of design rather than accreted packages.
The classic legacy blockers are simply absent. No .NET Framework anywhere in the solution, no System.Web, no Web Forms, no WCF, no COM interop, no stored-procedure layer: the engine's Platform Obsolescence and Internal Integration categories fired nothing. The data layer runs EF Core code-first with migrations. A service layer, repository interfaces, and dependency-injection registration are all mechanically confirmed present. Where the engine did find work, it is hygiene and deployment posture, not structural rewrites: five package references whose versions cannot be resolved (two MSBuild variable tokens and one package returning no versions from the feed -- the run's highest-priority finding), nine deprecated package references across six packages, two of which (AutoMapper.Extensions.Microsoft.DependencyInjection, Azure.Identity 1.16.0) sit in production projects, direct HttpClient use across seven UI-layer files, four IMemoryCache usages of which two back auth revocation, and twelve seed-data image URLs still pointing at a placeholder hostname.
A modernization effort here is evolution, not migration: dependency updates, an HTTP abstraction seam, a distributed-cache decision, and configuration externalization -- bounded, sequenceable work inside a stable architecture. The adequacy question -- does the suite that exists cover the layers that would change -- was measured rather than left open: the domain core and API are genuinely covered, the web host partially, and the Blazor admin, where the one real refactor lands, effectively not at all. That specific gap, together with the dependency cluster, is what holds the score at 77 instead of Green. The one thing that breaks first is not the architecture at all. It is the build, the first time the dependency floor shifts.
Modernization Readiness Score
The score is the senior architect's synthesis of the engine's signals: the architect assigns the 0-to-100 number from the dimension reads, then labels it with the Red/Yellow/Green band on the canonical Evincia scale (Red 0–49 / Yellow 50–79 / Green 80–100). The engine did not compute it.
Yellow, three points short of Green -- and the distinction between the layers is the point. The engine's signal picture leans Green: zero platform rule fires, clean integration categories, a real test suite. The 0-to-100 number lands one band below that, because the judgment layer weighs what signals cannot: whether the safety net covers the code the work would change (measured: not yet), and what the dependency decisions actually cost. Each dimension below is read on a 0–25 scale (higher = more ready); the four reads sum to 77.
| Dimension | Architect read (0–25) | What drives it here |
|---|---|---|
| Platform Obsolescence | Minimal · 23/25 | Zero rule fires. All ten projects net8.0 (LTS, in support at the run date -- with a calendar: .NET 8 LTS support ends 2026-11-10, so the one-hop move to .NET 10 is scheduled maintenance, not remediation); no System.Web, no Web Forms, no Windows-only API findings. |
| Architectural Coupling | Contained, with named exceptions · 21/25 | Direct HttpClient in seven UI-layer files (Pri 20) is the real item; the Web/PublicApi → Infrastructure references were routed by the engine at Pri 6 medium ambiguity and ruled after a source read: contained -- composition-root wiring plus standard identity plumbing, with the domain core identity-free; the two 250+ line methods are generated EF migration snapshots. Service layer, repositories, and DI are mechanically confirmed present. |
| Dependency Risk | Elevated at the hygiene level · 18/25 | The run's top finding -- five unresolvable package versions (Pri 25) -- was verified against the build system: they resolve (the variables are defined in central package management; the feed serves the flagged package). What remains real: nine deprecated package references (six packages) incl. Azure.Identity 1.16.0 in the web host (Pri 16) and an AutoMapper migration with a licensing dimension; no known-CVE packages, no version conflicts. |
| Change Safety | Moderate, measured · 15/25 | Four test projects, all 74 tests passing -- and coverage was measured, not assumed: 29.8% line overall, with the domain core at 80.9% and the API at 78.5%, the web host at 45.3%, and the Blazor admin at 1.1%. The net is strongest where the system is healthiest and absent where the work is. Placeholder seed URLs (Pri 16); database triggers unconfirmable from code; logging sink unconfirmed. |
Risk register — the critical band
Presented in the 12-field LMRR schema; only the representative columns are shown here. Priority is the engine's severity score for each finding -- Probability (1–5) × Impact (1–5), so it runs 1–25 and a higher number is more severe. On this codebase the Critical band (Priority 20–25) holds two rows -- on the legacy sibling it holds five. Every cited rule fired at Full confidence.
| ID | Finding | Category | Priority | Recommended action |
|---|---|---|---|---|
| R-001 | Five unresolvable package versions (two MSBuild variable tokens; one package with no versions on the feed) | External Dependency Risk | 25 | Verified 2026-07-17: the variables resolve (central package management defines both) and the feed serves the package. The finding stands as the engine's honest inventory-level read; the check closed it. |
| R-002 | Direct HttpClient use across seven UI-layer files (Web + Blazor admin) | Structural Coupling | 20 | Introduce typed clients / an abstraction seam before any service decomposition; the Blazor admin's auth and data services first. |
The High band (Priority 15–19) is hygiene and posture, not structure: the deprecation cluster (nine package references across six packages), the in-memory cache in auth paths, the placeholder seed URLs, generated EF snapshot methods, and mutable static state confined to two test projects.
Modernization Risk Profile
Illustrative assessment based on observed patterns. These are qualitative risk levels, not measured client scores.
What will break first
- The build, the first time the dependency floor shifts. The version tokens the engine flagged were verified to resolve today -- but the AutoMapper DI extension is deprecated with a changed registration model,
Azure.Identityis behind current releases, and the test frameworks carry deprecations of their own. The first major dependency bump, or the .NET 10 upgrade due before November, is where this surfaces -- as a broken build rather than a design discussion. - Auth revocation, on the first scale-out. Two of the four direct
IMemoryCacheusages in the web host sit in the authentication-revocation path. In-memory cache state is per-process; run two instances behind a load balancer and a revoked session stays live on the node that did not process the logout. That is a correctness failure, and a quiet one. - Catalog images, on the first hosting change. Twelve seed-data URLs and a base URL point at
http://catalogbaseurltobereplaced. The hostname says exactly what was supposed to happen to it. Any environment where the replacement step did not run serves a catalog of broken images.
Suggested modernization sequencing
- Pin the dependency floor. Resolve the two version variables and the
Blazored.LocalStoragefeed question, moveAzure.Identityforward, and decide the AutoMapper migration path. Everything else builds on a build that is known to resolve. - Extend the net to where the work lands. Coverage is measured, not assumed: the core and API are covered, the web host is half-covered, the Blazor admin is not (1.1%). Put coverage over the Blazor admin's auth and data services and the web host's cache paths before the HTTP-seam refactor, and remove the mutable static state in the two test projects so suite results are order-independent.
- Put a seam under outbound HTTP. Replace direct
HttpClientusage with typed clients or a service abstraction, starting with the Blazor admin's auth and data services where the usage concentrates. - Make the deployment posture explicit. Adopt a distributed cache or document the single-instance constraint, and externalize the seed/asset base URLs to configuration so hosting changes are config changes.
- Close what remains open. The identity-type placement ruling and the abstraction-consistency confirmation are closed (source read, 2026-07-17: contained; sole data path). Still open on a real deployment: the database-trigger inventory (a public teardown has no live database to inspect) and the logging-sink decision.
Confidence & coverage
This run achieved full semantic coverage: all ten projects loaded with semantic models via solution load (10 of 10), zero syntax-only fallback, zero skipped projects, and every cited rule fired at Full confidence. The one workspace diagnostic is the solution's docker-compose.dcproj, which is not a code project and does not affect analysis of the ten code projects. This was an engagement-mode run with full coverage enforced -- the confirming bar for client-grade use -- and the AI-drafted narrative passed the machine grounding gate with no corrections required. The 0–100 score and the four dimension reads are senior architect judgment, not mechanical output; the three judgment categories (Migration Feasibility, Embedded Business Logic, Domain Knowledge) are senior architect review by definition.
Two verification adjuncts were run outside the engine and are cited where used, never as engine signals: test coverage was measured on the pinned commit (all 74 tests passing; the per-assembly figures appear in the score table), and the unresolvable-version finding was checked against the build system -- both MSBuild variables are defined in central package management and the flagged package restores from the live feed, so the run's top finding is benign in the actual build environment while remaining true at the package-inventory level the engine reads.
What the engine did not claim
On the legacy profiles, the transparency section documents the false positives we corrected. This run needed no corrections -- so the honest disclosure here is the opposite one: what the engine deliberately declined to assert.
- Two boundary findings were routed, not asserted. The web host and the API both reference the infrastructure project. An earlier generation of the engine rated exactly this pattern Critical on another codebase -- and the senior architect's code read overruled it (that dispute is documented in the SocialGoal profile and the sample report). The engine now classifies where such a reference is exercised; here it read composition-root wiring plus one shared identity type, scored the findings low (Priority 6), flagged them medium-ambiguity, and handed them to the architect. The ruling landed after a source read: contained -- composition-root wiring and standard identity plumbing, with the domain core identity-free. The engine routed; the architect ruled; nothing needed a published correction.
- Two 250+ line methods were reported with their nature visible. Both are generated EF Core migration snapshots. The finding names them; the recommended action is to confirm generated status and deprioritize, not to refactor generated code.
- Test existence was confirmed; test adequacy was not invented. Four test projects, a 0.67 production ratio, two frameworks -- and an explicit statement that coverage cannot be measured statically. The adequacy read belonged to the senior architect, who measured it instead of guessing: the per-assembly figures in the score table are from an actual coverage run, and they are what kept this score out of Green.
A diagnostic earns trust in both directions: by proving the blockers it asserts on a legacy system, and by not manufacturing blockers on a healthy one.
What is mechanical, and what is senior architect judgment
The engine emits deterministic raw signals across nine automated risk categories and scores findings on fixed rules (Probability 1–5 × Impact 1–5 = Priority 1–25). Three further categories -- Migration Feasibility, Embedded Business Logic Risk, and Domain Knowledge Risk -- are senior architect review only; the engine produces no findings for them. The 0-to-100 score and the four dimension reads are senior architect judgment, synthesized from the signals. The engine does not compute the number, and this profile does not invent one.
eShopOnWeb illustrates that boundary in the opposite direction from the legacy profiles. There, the engine's job was to prove the blockers are real. Here its job is to prove the absence of blockers is real: zero platform findings, zero internal-integration findings, and a mechanically confirmed service seam are results of a full-semantic load, not impressions. The adequacy questions were then closed with evidence rather than left open: coverage was measured (the net covers the core, not the refactor surface), and a source read ruled the boundary references contained.
The three judgment categories were reviewed for this profile. Migration Feasibility: scheduled maintenance with a calendar -- a one-hop move to .NET 10 due before the 2026-11-10 end of .NET 8 support, with central package management making the framework bump one file; the AutoMapper replacement (a registration-model change with a commercial-licensing dimension) and endpoint-style consolidation ride the upgrade rather than block it. Embedded Business Logic: clean in the direction that matters -- services, specifications, and a delegating UI -- and honest in the other: as a teaching sample, some business behavior exists only as stubs (a hardcoded ship-to address at the checkout call site; no payment flow). Domain Knowledge: as low-risk as the category gets, because the code is the curriculum, documented in Microsoft's own architecture guidance -- which also means a public teardown can note this category as examined, not exercised the way a client engagement exercises it.
Modernization lesson
"Modern" is not the absence of findings; it is findings you can schedule. On this codebase every critical item is a bounded task inside a stable architecture -- on its legacy sibling, the critical items are the architecture. And bounded work is still work: the score stops at 77 because the ledger does.
A risk instrument you can trust on the systems that scare you is one that stays calm on the systems that should not -- and one that does not round a 77 up to Green for the sake of a tidy story. The Red readings on the legacy profiles are worth exactly as much as the discipline shown here.
Read the paired eShop LegacyWebForms profile → · Review all public codebase teardowns →
About this profile
Source: the Evincia diagnostic engine (commit 8fbc30e), Output A (raw signals) and Output B (narrative). Findings paraphrase the codebase; see the repository at the pinned commit for the original source. Analyzes MicrosoftLearning/eShopOnWeb under the MIT License; attribution preserved. This is an illustrative readiness profile of a Microsoft reference application, not an engagement and not a judgment of its maintainers. Part of the Evincia Public Codebase Teardowns program.
