feat(modelling): expose scenario/product/plan repos on the UnitOfWork (#1157)

Slice 4a. The Modelling stage reads the Scenario + Product catalogue and
writes the Plan + its Plan Measures on one session, committed once
(ADR-0012/0017). Adds uow.scenario / uow.product / uow.plan to the
UnitOfWork port and constructs them in PostgresUnitOfWork.__enter__.
Additive — existing stages and the bare-stub Modelling wiring are
unaffected. Wiring test asserts the unit exposes the three ports.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-06-03 11:53:34 +00:00
parent d66f7eed84
commit e778d1fb97
3 changed files with 33 additions and 0 deletions

View file

@ -10,9 +10,16 @@ from repositories.property_baseline.property_baseline_postgres_repository import
PropertyBaselinePostgresRepository,
)
from repositories.epc.epc_postgres_repository import EpcPostgresRepository
from repositories.plan.plan_postgres_repository import PlanPostgresRepository
from repositories.product.product_postgres_repository import (
ProductPostgresRepository,
)
from repositories.property.property_postgres_repository import (
PropertyPostgresRepository,
)
from repositories.scenario.scenario_postgres_repository import (
ScenarioPostgresRepository,
)
from repositories.solar.solar_postgres_repository import SolarPostgresRepository
from repositories.unit_of_work import UnitOfWork
@ -36,6 +43,9 @@ class PostgresUnitOfWork(UnitOfWork):
self.epc = epc_repo
self.solar = SolarPostgresRepository(self._session)
self.property_baseline = PropertyBaselinePostgresRepository(self._session)
self.scenario = ScenarioPostgresRepository(self._session)
self.product = ProductPostgresRepository(self._session)
self.plan = PlanPostgresRepository(self._session)
return self
def __exit__(

View file

@ -6,7 +6,10 @@ from typing import Optional
from repositories.property_baseline.property_baseline_repository import PropertyBaselineRepository
from repositories.epc.epc_repository import EpcRepository
from repositories.plan.plan_repository import PlanRepository
from repositories.product.product_repository import ProductRepository
from repositories.property.property_repository import PropertyRepository
from repositories.scenario.scenario_repository import ScenarioRepository
from repositories.solar.solar_repository import SolarRepository
@ -26,6 +29,11 @@ class UnitOfWork(ABC):
epc: EpcRepository
solar: SolarRepository
property_baseline: PropertyBaselineRepository
# Modelling-stage repos (ADR-0017): read the Scenario, read the Product
# catalogue, write the Plan + its Plan Measures — all on the one session.
scenario: ScenarioRepository
product: ProductRepository
plan: PlanRepository
@abstractmethod
def commit(self) -> None: ...

View file

@ -9,7 +9,10 @@ from sqlmodel import Session
from datatypes.epc.domain.epc import Epc
from domain.property_baseline.property_baseline_performance import PropertyBaselinePerformance
from domain.property_baseline.performance import Performance
from repositories.plan.plan_repository import PlanRepository
from repositories.postgres_unit_of_work import PostgresUnitOfWork
from repositories.product.product_repository import ProductRepository
from repositories.scenario.scenario_repository import ScenarioRepository
def _session_factory(db_engine: Engine) -> Callable[[], Session]:
@ -60,6 +63,18 @@ def test_an_exception_in_the_block_rolls_the_batch_back(db_engine: Engine) -> No
assert uow.property_baseline.get_for_property(10) is None
def test_unit_exposes_the_modelling_repos_bound_to_its_session(
db_engine: Engine,
) -> None:
# Arrange / Act
with PostgresUnitOfWork(_session_factory(db_engine)) as uow:
# Assert — the Modelling stage reads Scenario + Product and writes Plan
# through the same unit (ADR-0017).
assert isinstance(uow.scenario, ScenarioRepository)
assert isinstance(uow.product, ProductRepository)
assert isinstance(uow.plan, PlanRepository)
def test_leaving_the_block_without_commit_persists_nothing(db_engine: Engine) -> None:
# Arrange
new_unit = lambda: PostgresUnitOfWork(_session_factory(db_engine))