Model/docs/adr/0002-property-aggregate-root.md
2026-05-13 21:26:18 +00:00

1.7 KiB

Property is the aggregate root, not EpcPropertyData

The Ara modelling pipeline produces nine slices of per-property data (EPC, geospatial, solar, baseline performance, recommendations, optimised package, etc.). We considered making EpcPropertyData — the rich RdSAP-21-style EPC schema — the centrepiece, with other data hanging off it. We rejected that and introduced a new Property aggregate root that holds identity, all source data (EPC, Site Notes, Landlord Overrides), enrichments, and modelling outputs as named fields. Services take Property (or Properties) and return them with one slice populated.

Two reasons drove this:

  1. Geospatial, solar, recommendations, and overrides are peers to the EPC, not properties of it. Putting them on EpcPropertyData conflates physical-state schema with modelling-run state.
  2. A typed ModellingContext dict-bag (the obvious alternative) is exactly what the current legacy Property class became — 1259 lines of accumulated stuff, hard to read, hard to test, hard to extend. Named fields on a dataclass force the type system to keep us honest.

The cost is more domain types up front (Property, Properties, PropertyIdentity, BaselinePerformance, OptimisedPackage, etc.) and the discipline of one service writing one slice. The benefit is that every service has a single job and every test injects fake repos against a small, named structure.

Consequences

  • Every service signature accepts or returns Property / Properties. Refactoring later means touching all of them.
  • EpcPropertyData stays a pure physical-state schema (defined in datatypes/epc/domain/epc_property_data.py) — no modelling outputs or run state on it.