Skip to content

refactor(arch): group code by module folder, with contracts in a Contracts folder #1087

Closed

Description

@mforce

Goal

Group the code by module, so each module's files live together and its public contract sits in a clearly named folder. The projects stay as they are (Domain, Application, Infrastructure and Api); this is not an assembly split (#859).

Today code is grouped by feature (Features/Customers, Features/Sales, …), and the module ledger maps scattered folders onto modules. Example: src/Cluckwork.Application/Features/Customers/UpdateCustomer/UpdateCustomerCommand.cs belongs to Commerce, but nothing in its location says so.

Target shape (exact names to be settled by the design):

src/Cluckwork.Application/Modules/Commerce/
  Contracts/            ← what other modules may use
  Customers/UpdateCustomer/...
  Sales/ConfirmSale/...
src/Cluckwork.Domain/Modules/Commerce/...
src/Cluckwork.Infrastructure/Modules/Commerce/...

Namespaces follow folders. Platform and shared code get their own place.

Why

Package-by-module is standard modular-monolith practice. It should also simplify the rules:

The edges between modules, with their reasons, stay declared.

Done when

A design is in progress and will be linked here before implementation starts.

Activity

  1. added
    enhancementNew feature or request
    epic-514Modular monolith architecture (#514)
    track:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation
    on Oct 5, 2026
  2. added this to the Modular monolith milestone on Oct 5, 2026
  3. mforce commented on Oct 5, 2026

    @mforce
    OwnerAuthor

    Design approved by the owner on 2026-10-05. Astra reviewed it, and its corrections are applied.

    Layout. Each project gets Modules/<Module>/<feature>/…, and namespaces follow folders. Every module's public types live in Contracts/: in Application for most, and in Domain for 13 enums that entities use. Handlers and validators stay in the module's feature folders. Endpoints stay in the Api project, grouped as Api/Modules/<Module>/, and are still treated as Platform-owned adapters that may only use contracts.

    Inventory. 362 of 556 C# files under src/ move, and 194 stay shared. 22 mixed files are split first, which creates 39 new files. A file's placement follows the module rules plus documented placement decisions.

    Rules after the move

    • "Contracts folder = contract" replaces the 160 [ModuleContract] marks, which are removed module by module.
    • A new namespace/folder agreement test stops anyone from moving a file out of Contracts/ while keeping its namespace.
    • The <Owner>ModuleRules files stay, holding fewer rows.
    • Still declared: the edges with their reasons, Farm's seam, the table-owner exceptions, the foreign keys, adapter reach and the tenant-bypass rows.

    Safety, as measured in a spike on main

    • The namespace rewrite left the EF model unchanged, and a deliberate column change was caught.
    • 6 filter-free identities in IdentityProvider change hash only because a fully qualified namespace changed. They are re-keyed with a namespace-only proof. The 40 allow-list hashes stay unchanged.
    • The deploy repo uses only service_name="Cluckwork.Api", with no logger-category filters, so changed category names affect nothing there.

    Slices, one PR each, each with a short merge window against fresh main

    1. S0: preparation. Five guards that filter by namespace are made module-safe, including ServingGuardCoverageTests, so a moved module can never be skipped silently. The PR also adds the folder agreement test and a trial split.
    2. Pure-move slices: Insights, Finance, Flock Management, General Inventory, Egg Operations, Commerce, Farm, Access. Each changes no behaviour, proven by:
      • a clean build;
      • an unchanged EF model with no migration;
      • regenerated schema docs;
      • an identical ordered DI dump;
      • an identical OpenAPI document;
      • guard parity, with mutations still caught.
    3. Repositories and database mappings. This is the only slice that adds rules: 23 edge symbols become visible, including FlockScopeGuard (Access → Farm), plus one new cell, FlockManagement → EggOperations.
    4. Cleanup.

    The estimate is about 54 agent-hours, plus review and CI. It is provisional until S0 and S1 are done.

  4. added 20 commits that reference this issue on Oct 5, 2026
  5. mforce commented on Oct 6, 2026

    @mforce
    OwnerAuthor

    All slices are merged and main is green at 23bb192 (CI run 37407252236). Every module's code now lives under Modules/<Module>/, with its public types in Contracts/.

    Slice PR
    S0 module-safe guards #1088
    Insights #1089
    Finance #1090
    Flock Management #1091
    General Inventory #1092
    Egg Operations #1093
    Commerce #1094
    Access #1095
    Farm #1096
    S9 repositories and EF configurations (the one approved policy change: +23 edge symbols, +1 cell FlockManagement → EggOperations) #1097
    S10 cleanup: contracts by folder; [ModuleContract], Implementations and Types deleted; guards narrowed to the module roots; AGENTS.md and decision records updated #1099
    S11 architecture diagrams (deployment, code layers, feature modules) #1098

    Every move slice proved parity: the EF model was unchanged with no migration, the DI and OpenAPI output was identical, the contract set was identical, and the tenant-bypass hashes were unchanged except for the 6 namespace-only re-keys in #1096. Mutations showed that the guards still catch violations.

    Not done here, on purpose:

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

    enhancementNew feature or requestepic-514Modular monolith architecture (#514)track:C#514 Track C — moves production code; UNSCHEDULED, needs authorisation

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions