Skip to content

Experiment: slice 001 on the Clean Architecture Solution Template, counted against the twins - #15

Merged
erikshafer merged 4 commits into
mainfrom
experiment/before-clean-template
Sep 6, 2026
Merged

erikshafer merged 4 commits into
mainfrom
experiment/before-clean-template

Conversation

@erikshafer

Copy link
Copy Markdown
Owner

What this is

An experiment, not a slice: slice 001 (POST /cakes, GET /cakes, GET /cakes/{id}) rebuilt on the Clean Architecture Solution Template (dotnet new ca-sln, version 10.8.0), run against the same contract scenarios as a third host, and counted. It exists to test the slide-2.5 claim that the before twin is "the way the templates generate" rather than reword it.

It lives in experiments/before-clean-template/ and tests/LayerCake.ContractTests.CleanTemplate/, neither of which is in LayerCake.slnx. dotnet build and dotnet test at the root are exactly what they were on main; nothing under src/, openspec/, docs/slices/, or the shared scenario classes changed. The scorecard sections of docs/file-inventory.md are untouched; the experiment has its own section at the end, and docs/build-log.md has the full narrative.

The number

Same three endpoints, .cs files created / edited:

Host Created Edited Touched
Before twin 19 4 23
Template 9 8 17 (13 if the four Guid-key ripple edits count as a one-time cost)
After twin 5 1 6

Running it

dotnet test tests/LayerCake.ContractTests.CleanTemplate/LayerCake.ContractTests.CleanTemplate.csproj

Docker running is the only prerequisite (Testcontainers PostgreSQL, same fixture as the twins). Result as committed: 6 of 10 green (ping plus five cake scenarios). The four error scenarios (two 400s, 404, 409) return the right status codes and are red on content type alone: the template's exception handler writes application/json, never application/problem+json. The two 400 bodies also carry no field names; the template builds a ValidationProblemDetails and serialises it as the base type, so the errors dictionary is dropped. One line in the template's handler (Results.Problem(problemDetails).ExecuteAsync) makes it 10 of 10; that was probed and reverted so the branch shows the template as shipped.

The template host was also run live and driven from the repo's demo page; every route answered as the suite says.

Why the third host is a separate test project

Adding the template to LayerCake.ContractTests unified the test bin to MediatR 14.1.0 and AutoMapper 16.1.1, and every before-twin scenario failed with a TypeLoadException before running. One process, one MediatR. The separate project references the existing suite for the unchanged scenario base classes and aliases the template's Web assembly because all three hosts expose a public top-level Program.

Scaffold edits (outside the count)

  • global.json floor lowered to the installed 10.0.1xx SDK band.
  • NU1902;NU1903 appended to the template's own WarningsNotAsErrors: as generated on 2026-09-06 the template fails restore on NuGet audit warnings promoted to errors.

Open calls

  • Keep the branch at the honest 6 of 10, or apply the one-line problem+json fix as a listed scaffold edit.
  • Whether the four Guid-key ripple edits count as feature edits (17) or scaffold (13).
  • Whether an Ardalis variant (before-clean-arch) is worth about an hour.

…into experiments/before-clean-template

dotnet new ca-sln -n LayerCake.CleanTemplate -cf None -db postgresql -o experiments/before-clean-template
Template as generated, plus the two edits it took to build in place on this
machine: global.json floor lowered to the installed 10.0.1xx SDK band, and
NU1902/NU1903 audit warnings on the template's own transitive graph added to
the template's existing WarningsNotAsErrors list (TreatWarningsAsErrors is on).
Not part of LayerCake.slnx; not on the scorecard.
…late, the template's way

IApplicationDbContext instead of a repository, command and handler in one
file, DTO with a nested AutoMapper profile and ProjectTo on reads, FluentValidation
validator picked up by the template's pipeline, Guard.Against.NotFound for the
404, a new DuplicateCakeNameException mapped to 409 in the template's
ProblemDetailsExceptionHandler, an IEndpointGroup with RoutePrefix /cakes, seeds
in the template's initialiser. The template's int-keyed base entity goes generic
the way its own comment suggests; the two Todo samples move to
BaseAuditableEntity<int>. A /ping group is added for the shared ping scenario
and counted separately.
…gainst the template

tests/LayerCake.ContractTests.CleanTemplate is its own test project, outside
LayerCake.slnx: one test process can load one MediatR and one AutoMapper, and
the template's 14.1.0 / 16.1.1 unloaded the before twin's 12.5.0 / 14.0.0 when
the reference was added to the shared suite (every before-twin scenario failed
with a TypeLoadException before running). The twins keep their process; the
template host references the existing suite for the unchanged scenario base
classes and aliases the template's Web assembly because all three hosts expose
a public top-level Program. Same Testcontainers PostgreSQL fixture, own
database name because the template initialises with EnsureDeleted +
EnsureCreated. Result as committed: 6/10 green (5/9 cakes + ping); the four
error scenarios are red on content type alone (the template's exception
handler writes application/json), status codes correct.
… build-log entry

File inventory gains a final section outside the scorecard (9 created / 8 edited
for the same three endpoints, scaffold edits, snapshot, line counts); the build
log records the template version and command, the package graph, why the
generated template did not build, every idiom difference from the before twin,
the suite result and why four scenarios are red, the negative checks, the hop
trace, the two price-rule file lists, the live run, and the calls left to Erik.
@erikshafer erikshafer self-assigned this Sep 6, 2026
@erikshafer
erikshafer merged commit bec93f6 into main Sep 6, 2026
1 check passed
@erikshafer
erikshafer deleted the experiment/before-clean-template branch September 6, 2026 23:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant