Skip to content

refactor(arch): publish DiscountCeiling in Commerce's contract - #1118

Merged
mforce merged 1 commit into
mainfrom
refactor/1116-discount-ceiling-contract
Oct 6, 2026
Merged

mforce merged 1 commit into
mainfrom
refactor/1116-discount-ceiling-contract

Conversation

@mforce

@mforce mforce commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Part of #1116 (cleanup PR 1). It does not close the issue.

Moves DiscountCeiling from Domain/Modules/Commerce/Sales/ to Domain/Modules/Commerce/Contracts/, unchanged apart from its namespace. Farm stops reaching a Commerce type outside the contract.

CW1004 census (tools/architecture/cw1004-census.sh): 57 → 53. The 4 removed rows are Farm → Commerce.Sales.DiscountCeiling at Account.cs:90, FarmModule.cs:64, UpdateFarmSettingsHandler.cs:150 and UpdateFarmSettingsValidator.cs:98. Three ConfirmSaleHandler rows only moved down one line because of the added using. No other row changed.

Proofs and the policy review follow in an edit.

Policy review: why DiscountCeiling belongs in Commerce's public API

Moving a type into Contracts/ changes what the guards let peers and adapters name, so this is a policy change, not just a move.

  • It is already a cross-module value.

    • Farm owns the setting: Account.MaxDiscountBasisPoints is exposed as Account.MaxDiscount.
    • Farm validates and parses it: UpdateFarmSettingsValidator and UpdateFarmSettingsHandler use DiscountCeiling.TryParsePercent.
    • Farm reports it: FarmModule fills FarmSettingsDetails from .Percent.
    • Commerce applies it at confirm (ConfirmSaleHandler, SalesOrder).

    Publishing one type keeps one rule for parsing, range (MaxBasisPoints) and comparison. Otherwise Farm would need a parser of its own that could drift.

  • It is a value, not behaviour or state: a readonly record struct holding basis points, with parse, format and compare members. It names no entity, aggregate or persistence type. ModuleContractRealAssemblyTests now walks it (see proofs).

  • What it opens: any peer or adapter may now name DiscountCeiling. That is intended: the ceiling is part of the farm-settings surface that IFarmModule already exposes as a percent. It gives no access to orders, lines or the confirm path.

  • Not done: moving ownership of the type to Farm. That would be a design change, and Commerce would then reach into Farm's contract for its own pricing rule.

Proofs (head 38cefb15)

Check Result
Census 57 → 53. Exactly the 4 Farm → DiscountCeiling rows removed; 3 ConfirmSaleHandler rows moved down one line (added using)
Tenant-bypass re-pin none. No member names DiscountCeiling by qualified name; TenantBypassRealTreeTests 4/4 with the registries untouched
EF model dotnet ef migrations has-pending-model-changes: "No changes have been made to the model since the last migration" (AccountConfiguration ignores MaxDiscount; the snapshot never named the type)
Purity guard covers the promoted type adding SalesOrder? PurityProbe to DiscountCeiling makes ModuleContractRealAssemblyTests fail ("DiscountCeiling.FromBasisPoints exposes …SalesOrder via DiscountCeiling.PurityProbe"); restored
Guard classes (one at a time, all green) AdapterReachRealTreeTests 2, PeerContractRealTreeTests 1, ModuleContractRealAssemblyTests 1, ContractDerivationTests 1, ModuleLedgerRealTreeTests 2, CouplingMatrixRealTreeTests 3, ModuleEdgeAnalyzerTests 14, NamespaceFolderAgreementTests 10, AdapterTierRealTreeTests 3, AccessOwnershipClaimTests 2, InsightsReadOnlyTests 25, TenantBypassRealTreeTests 4, SeamSurfaceRealAssemblyTests 2, TableOwnerRealModelTests 2, CompatibilityExceptionRealTreeTests 2
Affected unit tests DiscountCeilingTests 38, SalesOrderCeilingTests 16, AccountSettingsTests 39, UpdateFarmSettingsValidatorTests 62
Build dotnet build Cluckwork.slnx with repo defaults: 0 warnings, 0 errors

Five now-unused using Cluckwork.Domain.Modules.Commerce.Sales; lines were removed: each removal was kept only if the solution still built. The Farm → Commerce edge reason in Modules/Farm.cs now names the type's new place; the edge's symbols are unchanged.

Part of #1116. Farm stores the ceiling on Account and validates it in its
settings handler, while Commerce applies it at confirm; both now read one
published value type from Domain/Modules/Commerce/Contracts. The CW1004
census falls from 57 to 53. Namespace move only: no behaviour change.
@mforce
mforce merged commit 2cf8deb into main Oct 6, 2026
19 checks passed
@mforce
mforce deleted the refactor/1116-discount-ceiling-contract branch October 6, 2026 18:43
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