Skip to content

Merge Quotinator.Engine into Quotinator.Core — collapse the three-project domain split to two #206

Description

@DutchJaFO

Background

ADR 004 was revised for issue #121 to split Quotinator.Engine out of Quotinator.Core, specifically
so Quotinator.Data could stay domain-agnostic while something still held Quotinator-domain entities
and SQL that needed to see both Core's domain types and Data's Dapper/SQLite infrastructure. That part
of the reasoning is sound and unaffected by this issue — Quotinator.Data stays exactly as it is.

The separate half of that decision — that Core itself must stay Dapper/SQLite-free, forcing the
domain-implementation code into a third project rather than letting Core depend on Data directly — has
turned out not to be worth its cost. Found while planning #192 (expose Series/Universe on the quote read
path): a response DTO needing both a Core-owned type (MasterDataReference) and a Data-owned type
(CompletenessStatus) cannot be expressed in either project alone, only in Quotinator.Api — which is
why every masterdata response DTO (SourceResponse, CharacterResponse, etc.) ended up in
Quotinator.Api.Models instead of alongside QuoteResponse in Quotinator.Core.Models. That's not a
one-off; it will recur for every future Core-layer type that also needs a Data-layer concept.

The boundary has also already blurred in practice, independent of #192:

  • Quotinator.Core.Tests.csproj already has a direct ProjectReference to Quotinator.Engine.csproj
    it is not a Core-only test project today.
  • tests/Quotinator.Core.Tests/Data/ already contains QuotinatorMigrationsTests.cs and
    SqliteQuoteServiceConversationTests.cs/SqliteQuoteServiceSearchTests.cs — tests for
    Quotinator.Engine.Database.QuotinatorMigrations and Quotinator.Engine.Services.SqliteQuoteService,
    filed under a folder name that matches neither Engine namespace.

Nothing else in this codebase consumes Quotinator.Core without also consuming Quotinator.Engine
Quotinator.Api references both together, and always has. The only projects that reference Core alone
are the converter plugins (Quotinator.Converters.RegexArray/Csv/BasicJsonArray), which need only
SourceQuote/IQuoteSourceConverter-shaped types and gain Dapper/Microsoft.Data.Sqlite as a transitive
package reference they don't use — an accepted cost, not a functional problem, consistent with this
project's Simplicity priority over minimal per-project dependency footprints.

What needs to be done

  1. Merge every file under src/Quotinator.Engine/ into src/Quotinator.Core/, renamespacing
    Quotinator.Engine.XQuotinator.Core.X for each segment (Entities, Queries, Repositories,
    Database, Services, Models, Helpers) per the File placement rule (folder name = namespace
    segment). Check for and resolve any folder-name collisions between Core's and Engine's existing
    Models//Services//Helpers/ folders (none currently expected, but confirm directly rather than
    assume).
  2. Delete src/Quotinator.Engine/Quotinator.Engine.csproj and its folder. Update
    Quotinator.Core.csproj: add the Dapper, Dapper.Contrib, and Microsoft.Data.Sqlite package
    references and the Quotinator.Data.csproj project reference (all currently on Engine's .csproj).
  3. Merge tests/Quotinator.Engine.Tests/ into tests/Quotinator.Core.Tests/, same renamespacing rule.
    While merging, fix the existing Data/ vs Database/ folder-naming mismatch found during this
    issue's investigation — tests move into the folder matching their type's actual namespace segment,
    not the folder they happened to be filed under. Delete Quotinator.Engine.Tests.csproj.
  4. Update src/Quotinator.Api/Quotinator.Api.csproj to remove the now-redundant Quotinator.Engine
    project reference (Core already brings in everything Engine provided). Update every
    using Quotinator.Engine.X; across Quotinator.Api (Program.cs, Endpoints/*.cs) to
    Quotinator.Core.X.
  5. Update Quotinator.slnx: remove the two deleted projects, confirm the remaining projects' folders
    still register correctly.
  6. Update docs/architecture-decisions/004-quotinator-data-project-boundaries.md with a new revision
    section recording this merge, its reasoning, and marking the project as two layers again
    (Quotinator.Data domain-agnostic infrastructure; Quotinator.Core everything Quotinator-domain,
    including its own Dapper/SQLite implementation) under Quotinator.Api.
  7. Update CLAUDE.md's "Project structure" section: remove the Quotinator.Engine/ bullet, update
    Quotinator.Core/'s description, and update the "Dependency direction" line
    (Quotinator.ApiQuotinator.Core; Quotinator.CoreQuotinator.Data; Quotinator.Api
    Quotinator.Constants). Update the "Masterdata reference shape" section — MasterDataReference and
    every masterdata response DTO can now live in one place (Quotinator.Core.Models), removing the
    Api/Core split entirely rather than adding a second parallel type, which is what unblocks Expose series/universe on the quote read path — QuoteResponse fields and filters #192.
  8. Verify: dotnet build --configuration Release → 0 warnings, 0 errors. Full test suite green,
    0 warnings, 0 errors. T2 required (touches Program.cs, package references, and the solution
    structure) — this project always runs T2 regardless of a documented trigger.

Expected tests

No new behaviour is introduced — this is a structural move. The bar is that the full existing test
suite (all of Quotinator.Core.Tests's and Quotinator.Engine.Tests's current tests, merged) passes
unmodified after the renamespacing, proving nothing's behaviour changed. SqlQueryGuardTests and
QuotinatorMigrationsTests/schema-drift tests are the highest-risk regression surface, since they
reflect over type names/namespaces directly — confirm both still pass against the merged assembly.

Definition of done

  • All files moved and renamespaced; no Quotinator.Engine namespace remains anywhere in the solution
  • Quotinator.slnx, Quotinator.Api.csproj, ADR 004, and CLAUDE.md all updated in the same change
  • Full test suite passes unmodified (merge is behavior-preserving)
  • Build clean (0 warnings, 0 errors); T1 + T2 both green
  • Findings summarised in a closing comment

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETPull requests that update .NET codecode styleStyle, consistency, or analyzer/tooling configurationenhancementNew feature or request

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions