02 · Case study
ASP.NET Core monolith.NET 10 · Aspire · YARP · EF Core · Wolverine
The system
The problem
One solution, one deployment, one database. Every feature touches several modules, checkout cannot be scaled without scaling the catalog, and the build is slow enough that people batch their changes. The team knows it wants services. What it does not have is a defensible answer to where the boundaries go — and the two usual answers are an eighteen-month rewrite, or a model that invents boundaries and hands back code that does not compile.
What ran
Projects and namespaces, EF Core entities and their relationships, DbContext boundaries, ASP.NET routes, the call graph, and every cross-context write. None of that is a question you answer — it is read from the code.
Whether this machine can build and test the code at all, before any model call, so a machine that cannot build stops early and costs nothing.
Deterministically, from data ownership and the call graph. Two runs over the same code produce the same map.
Thirty-two questions, all with a recommended default, each one changing real files. Then the plan. Stop here and nothing has been added to the repository except working notes.
Your existing code is moved into the new service projects rather than regenerated, and the places where the move breaks are marked. The model writes those seams — the typed clients, and the sagas for writes that used to be one transaction.
What it did
What lands
services/<ctx>/ one project per service with its Dockerfile and manifests · gateway/ YARP with edge auth and rate limiting · AppHost/ Aspire, so dotnet run starts the whole fleet locally · ServiceDefaults/ telemetry, health, resilience, discovery
Tests, generated not suggested
Integration tests per aggregate against a real database in a Testcontainer · health smoke test · architecture fitness test that fails the build if a domain type reaches for EF Core · contract boundary tests · equivalence replay of old path against new · schema guard · saga tests with compensation
Documents
ARCHITECTURE.md · STATE-FLOW.md · SEQUENCE-DIAGRAMS.md · RUNBOOK.md · docs/adr/ · MIGRATION-SCORECARD.md · one brief per service. Written after you approve the plan, not before.
Where it ended
A compiling solution, one commit per verified task, and a strangler route on
the gateway so anything no service has taken yet still goes to the monolith. A test
project that compiles but discovers zero tests is reported as a failure rather than a
pass — dotnet test exits 0 when it finds nothing, and that used to
read as green.
What happens next
The heavy structural work — where the boundaries are, what owns which data, what order to extract in — is deterministic and derived from the code. The model is pointed at the small genuinely hard part: reconnecting the seams and writing the sagas. That is why the result compiles.