Files
db-subclass-to-dto/yak/yak-01-before.md
T

3.9 KiB

Yak 01 — C# solution: the BEFORE situation (DB-backed domain classes)

Context

This exercise illustrates the transition from domain classes that inherit a DB base class (EF-style, or active-record-like) to plain domain objects + DTOs. See before-after-dto.mmd (and before-after-dto-sketch.png) for the diagram this yak implements in C#:

  • DbContext — the database context (EF fake)
  • DbBase — base class with an Id; depends on DbContext
  • Client, Order — domain classes inheriting DbBase
  • WebApp — consumes the domain classes directly

Yak 02 will implement the "after" situation in the same solution.

Tech constraints

  • .NET SDK: target net8.0 (adjust if only another SDK is installed; note it in the commit message).

  • Fake the EF magic — do NOT add real EF Core. The point of the exercise is the shape of the dependencies, not EF's behavior. A fake DbContext keeps the project pure managed code: no native binaries, no SQLite, no provider quirks → guaranteed cross-platform (Windows/macOS/Linux).

  • Tests: xUnit, one test project.

  • Solution layout:

    db-subclass-to-dto.sln
    src/
      Before/            (net8.0 class library, namespace Before.*)
      After/             (net8.0 class library, namespace After.*)  ← created in Yak 02
    tests/
      BeforeAfter.Tests/ (net8.0 xUnit project)
    

    Yak 01 only creates the solution, the Before/ project, and the test project (plus an empty After/ project so the solution shape is final).

"Magic" to fake on DbBase / DbContext

Simulate EF's change tracker with a minimal fake:

  • DbContext holds a registration dictionary of entities it knows about.
  • DbContext.Save() iterates tracked DbBase entities: assigns a fresh Guid Id to any entity whose Id is Guid.Empty, and records it.
  • DbBase carries a back-reference to its owning DbContext (active-record-style "I know which context created me") — this is the dependency leak the exercise wants to expose.
  • Client has an Orders collection; Order has a Client back-reference. Adding an order to client.Orders sets order.Client (mimic EF navigation fix-up).
  • A WebApp type with methods that consume Client/Order directly, including one that demonstrates the leak: reaching the DbContext through a domain object (e.g. client.DbContext).

Keep the fake small; this is an illustration, not an ORM.

Required tests (BeforeTests)

At minimum:

  1. Client and Order derive from DbBase (typeof(DbBase).IsAssignableFrom(typeof(Client)) etc.).
  2. New Client/Order instances have Id == Guid.Empty.
  3. DbContext.Save() assigns fresh, distinct Guid ids to saved entities (the faked EF magic).
  4. Saved entities are registered with their DbContext (reachable via the back-reference).
  5. Relation fix-up: client.Orders.Add(order) sets order.Client == client.
  6. The leak: from a bare Client, you can reach the DbContext (client.DbContext is non-null after save) — assert this explicitly, comment that this is exactly what the "after" situation removes.
  7. WebApp methods work on the domain classes directly.

Acceptance criteria

  • dotnet build db-subclass-to-dto.sln succeeds with 0 warnings.
  • dotnet test db-subclass-to-dto.sln succeeds; all tests pass.
  • No NuGet packages beyond xUnit's default set (no EF Core, no SQLite, no AutoMapper).
  • Code + tests committed with a message referencing this yak.

Open questions (answer before starting, defaults in parens)

  1. Target framework net8.0 or something else? (net8.0)
  2. Fake DbContext only, or do you also want a variant using real EF Core + Microsoft.Data.Sqlite (bundled native lib — still no install needed on Windows, but adds NuGet/ABI surface)? (fake only)
  3. Any naming/style preference (file-scoped namespaces, records vs classes)? (file-scoped namespaces; plain classes)