Skip to content

refactor(arch): move repositories and EF configurations into their modules - #1097

Merged
mforce merged 3 commits into
mainfrom
refactor/1087-persistence-ownership
Oct 6, 2026
Merged

mforce merged 3 commits into
mainfrom
refactor/1087-persistence-ownership

Conversation

@mforce

@mforce mforce commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Refs #1087

S9 of the package-by-module design: the 26 repository files and 13 EF-configuration files move into Infrastructure/Modules/<Owner>/{Repositories,Configurations}. This is the one slice that changes the dependency policy, and the owner approved it. The change declares 23 edge symbols and one new cell, and empties every Implementations list. No runtime behaviour changes. Proofs compare this head 247e259b with the base 57cdbc6b (#1096).

Review order

  1. move (generated; skim it): 39 git mvs, with namespaces taken from folders. tools/module-move/move.py rewrote references, usings, ledger strings (edge symbols, Implementations, tenant-bypass Symbol/File) and paths, then pruned unused usings.
    • UserRoleAssignmentRepository.cs goes to Access whole, FlockScopeGuard included, and so does UserRoleAssignmentConfiguration.
    • The six rules files that lacked one gain their Cluckwork.Infrastructure.Modules.<Owner> root.
    • This commit alone fails CW1001 on exactly the 23 references the next commit declares. The analyzer's demand is what makes the policy change explicit.
  2. policy (read this one in full): src/Cluckwork.Domain/Common/Architecture/Modules/*.cs, the FK reason in RealModuleLedger.Tables.cs, the regenerated coupling-matrix.md, and one guard line in CompatibilityExceptionRealTreeTests.
  3. docs: fixes one layer-relative EggLotRepository path in docs/architecture.md.

The policy change

The measured count is 23 symbols (22 in existing cells) + 1 new cell. It matches Astra's design-time attribution symbol for symbol (pbm-astra-evidence/edges-reproduction.log).

Cell New symbols What they reference
Access → Commerce ApplicationUserConfiguration EggUnit stepper-unit column
Access → Farm UserRoleAssignmentConfiguration, UserRoleAssignmentRepository, FlockScopeGuard UserRoleAssignment; Roles.ResolveEffective, EffectiveAccountRole
Commerce → EggOperations ProductEggGradeMappingConfiguration, SalesOrderItemConfiguration, SalesOrderAllocationConfiguration FKs to EggGrade, EggLot
Commerce → Farm PaymentRepository, ProductRepository, SalesOrderRepository implement ICurrencyBoundRowSource
EggOperations → Farm EggOperationsFixture SeedDefaults
Farm → Commerce AccountConfiguration EggUnit column, ignored DiscountCeiling
Finance → Farm ExpenseRepository ICurrencyBoundRowSource
Finance → FlockManagement ExpenseConfiguration FK to Flock
GeneralInventory → EggOperations FeedUsageConfiguration, WaterUsageConfiguration FKs to DailyEntry
GeneralInventory → Farm FeedUsageRepository, InventoryItemRepository, InventoryLotRepository ICurrencyBoundRowSource
GeneralInventory → FlockManagement FeedUsageConfiguration, InventoryMovementConfiguration, WaterUsageConfiguration FKs to Flock
FlockManagement → EggOperations (new cell, R) BirdMovementConfiguration FK to DailyEntry

Each cell's reason gains one sentence naming its new symbols. New symbols are appended, so the existing lines stay unchanged apart from a trailing comma. The FK_BirdMovements_DailyEntries_DailyEntryId reason no longer says that no Flock Management code references Egg Operations; it names BirdMovementConfiguration and the new cell.

Implementations is empty in every rules file, so a repository now reads its tables as its own module's code. CompatibilityExceptionRealTreeTests asserted that at least one read was allowed through an Implementations row. That case cannot exist any more, so the line now asserts that a moved repository's read is walked and allowed as an own-module read.

TrackedMutationReadTests needed no change. S0 (#1088) already selects Repositories/ and Modules/<Owner>/Repositories/ with an exact discovery comparison, exclusions and floor; a mutation below proves it covers the new location.

Checks at the head (base measured on 57cdbc6b)

Check Base Head
dotnet build Cluckwork.sln (analyzers on) 0 warnings 0 warnings
Application.Tests Architecture + TenantBypass + Documentation + ImagePin 474/474 474/474
Domain.Tests 495/495 495/495
Integration: ServingGuardCoverage, TrackedMutationRead, InsightsReadOnly, GlobalUserLookupGuard, FlockScope classes, BusinessRecordChronology* 49/49 49/49

CI at 247e259b: 18 checks pass, including the four test legs, "Build, audit and schema docs", both image smoke tests and the three Playwright shards. "Publish the commit image" is skipped, as on every PR.

What differs from main, and nothing else

Report Result
Module-edge report (live edges) +23, −0: exactly the 23 declared symbols above. No other edge appears or disappears
coupling-matrix.md regenerated; diff below. One new cell plus the new symbol counts, which add up to 23
Peer-reach report +60 rows, −0. 57 are same-module: the peer walk now covers the moved files as module code. The 3 cross-module rows are Access → Farm through UserRoleAssignment, which is on Farm's Seam. The remaining additions are framework types the walk does not resolve (CancellationToken, Guid, EntityTypeBuilder, …). PeerContractRealTreeTests green
-| Access | — | W (18) fk:1 | R (3) | W (1) | W (6) | — | — | — | P |
-| Farm | — | — | — | — | R (5) | — | — | — | P |
-| FlockManagement | — | R (1) | — | — | — | — | — | — | P |
-| EggOperations | — | R (2) | W (4) | — | — | — | — | — | P |
-| Commerce | R (1) | R (4) | — | W (5) fk:3 | — | — | — | — | P |
-| GeneralInventory | — | R (3) | R (3) fk:3 | R (2) fk:2 | — | — | — | — | P |
-| Finance | — | R (3) | R (2) fk:1 | — | — | — | — | — | P |
+| Access | — | W (21) fk:1 | R (3) | W (1) | W (7) | — | — | — | P |
+| Farm | — | — | — | — | R (6) | — | — | — | P |
+| FlockManagement | — | R (1) | — | R (1) fk:1 | — | — | — | — | P |
+| EggOperations | — | R (3) | W (4) | — | — | — | — | — | P |
+| Commerce | R (1) | R (7) | — | W (8) fk:3 | — | — | — | — | P |
+| GeneralInventory | — | R (6) | R (6) fk:3 | R (4) fk:2 | — | — | — | — | P |
+| Finance | — | R (4) | R (3) fk:1 | — | — | — | — | — | P |

Parity proofs (base 57cdbc6b vs head 247e259b)

Proof Result
EF migrations has-pending-model-changes "No changes have been made to the model since the last migration." The model snapshot is untouched: configurations are not entities, so no CLR name in it moved, and Migrations/ has no diff at all. Control: Flock.Name at HasMaxLength(Flock.MaxNameLength + 1) in the moved FlockConfiguration makes the same command exit 1, which proves ApplyConfigurationsFromAssembly still finds the moved configurations
tools/schema-docs/generate.sh --check "docs/schema/ is up to date"
Ordered DI descriptors: Production/Development × serving/one-shot × Redis absent/configured (8 dumps) identical after the type map
Generated OpenAPI document (Development) identical
Exact normalized contract set per owner identical (160 types)
Adapter-reach report identical
Table-owner report identical: entity namespaces decide table owners, and no entity moved
Tenant-bypass registry allow-list 40 → 40 and filter-free 36 → 36 rows, identical after the map. 26 allow-list rows re-keyed (Symbol/File only). No Hash or Signature changed
Unused usings (IDE0005, whole solution) base 198, head 198, 0 new

FlockScopeGuard is unchanged (auth path)

The source diff of UserRoleAssignmentRepository.cs is the rename plus one line: namespace Cluckwork.Infrastructure.Repositories; → namespace Cluckwork.Infrastructure.Modules.Access.Repositories;. There are no logic edits.

Tests Base Head
Application FlockScopeGuardTests 1/1 1/1
Domain RolesTests 11/11 11/11
Integration FlockScopeTests, FlockScopeMiddlewareTests, AccessAuthorizationContractTests, AccessLookupTests 41/41 41/41

Mutations

Each case ran in a spare worktree at the head. It was reset and built, then run against the Architecture + TenantBypass filter (C5: TrackedMutationReadTests). When the build itself failed, the case was rebuilt with analyzers off, so the guards still ran.

# Mutation Result
C1 moved ExpenseRepository gains a read of Commerce's db.SalesOrders (no Finance → Commerce edge, no Implementations row left) red CompatibilityExceptionRealTreeTests.RealSourceTree_EveryCompatibilityExceptionIsRegistered. The build and every other guard stay green, so this guard is the only one that catches a cross-module table read
C2 moved FlockRepository references Finance's Expense (no FlockManagement → Finance edge) build red CW1001. With analyzers off: red ModuleLedgerRealTreeTests, CouplingMatrixRealTreeTests (2)
C3 FlockScopeGuard deleted from the Access → Farm cell build red CW1001 naming FlockScopeGuard; red ModuleLedgerRealTreeTests, CouplingMatrixRealTreeTests
C4 the new FlockManagement → EggOperations cell deleted build red CW1001 naming BirdMovementConfiguration; red ModuleLedgerRealTreeTests, CouplingMatrixRealTreeTests (2)
C5 moved FlockRepository.GetByIdAsync reads AsNoTracking() red TrackedMutationReadTests.AllMutableRepositoryReads_AreTracked and FlockRepository_GetByIdAsync_ReturnsATrackedEntity: the tracked-read guard covers Modules/<Owner>/Repositories/

mforce added 3 commits October 6, 2026 01:50
…dules

move.py move with the slice's table: the 26 repository files go to
Infrastructure/Modules/<Owner>/Repositories and the 13 EF-configuration
files to Infrastructure/Modules/<Owner>/Configurations, with the namespace
taken from the folder. UserRoleAssignmentRepository.cs, FlockScopeGuard
included, and UserRoleAssignmentConfiguration go to Access, which owns the
table. The six rules files without one gain their
Cluckwork.Infrastructure.Modules.<Owner> root. The current model snapshot
is untouched: configurations are not entities.

This commit alone fails CW1001 on 23 references the moved types now make
across modules; the next commit declares them.

Refs #1087
…make

The one slice that changes the dependency policy (owner-approved). Moving
the 39 persistence files into their modules makes their types module-owned,
so the references they already made now cross owners:

- 22 edge symbols join existing cells: EF configurations' foreign keys and
  enum mappings into other modules' entities, repositories implementing
  Farm's ICurrencyBoundRowSource, EggOperationsFixture's SeedDefaults, and
  in Access -> Farm the UserRoleAssignment store and mapping plus
  FlockScopeGuard, which resolves the effective role through Farm's Roles.
- One new cell, FlockManagement -> EggOperations, for
  BirdMovementConfiguration's foreign key to DailyEntry. The
  FK_BirdMovements_DailyEntries_DailyEntryId reason now names it.

Every Implementations list is empty: a repository reads its tables as its
own module's code. CompatibilityExceptionRealTreeTests' non-vacuity check
now asserts that a moved repository's read is walked as an own-module read.
The coupling matrix is regenerated: one new cell and the new symbol counts.

Refs #1087
…sitories

docs/architecture.md names EggLotRepository by layer-relative path, which
the codemod's project-relative rewrite does not match.

Refs #1087
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