From 2fbd7147b76c377122adfd6b4eb2495becf8aa63 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 3 Jun 2026 22:44:48 +0000 Subject: [PATCH] refactor(modelling): move PortfolioGoal to domain/modelling/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PortfolioGoal is domain vocabulary (a Scenario's goal — legacy planning branches on PortfolioGoal.INCREASING_EPC), so it belongs in domain/ co-located with scenario.py, mirroring how domain/epc/wall_type.py holds an enum that infrastructure/ imports. This lets the consolidated ScenarioModel (next slice) source the goal enum from domain without an infra→backend dependency. portfolio.py keeps a re-export so every existing `from ...portfolio import PortfolioGoal` caller is unaffected. Co-Authored-By: Claude Opus 4.8 --- backend/app/db/models/portfolio.py | 13 +++++-------- domain/modelling/portfolio_goal.py | 23 +++++++++++++++++++++++ 2 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 domain/modelling/portfolio_goal.py diff --git a/backend/app/db/models/portfolio.py b/backend/app/db/models/portfolio.py index 452c8d36..58aa2535 100644 --- a/backend/app/db/models/portfolio.py +++ b/backend/app/db/models/portfolio.py @@ -18,6 +18,11 @@ from backend.app.db.models.users import UserModel # noqa from backend.app.db.models.materials import MaterialType from datatypes.epc.domain.epc import Epc +# PortfolioGoal moved to the domain layer (ADR-0017 amendment). Re-exported here +# so the existing `from backend.app.db.models.portfolio import PortfolioGoal` +# callers keep working. +from domain.modelling.portfolio_goal import PortfolioGoal # noqa: F401 + class PortfolioStatus(enum.Enum): SCOPING = "scoping" @@ -32,14 +37,6 @@ class PortfolioStatus(enum.Enum): NEEDS_REVIEW = "needs review" -class PortfolioGoal(enum.Enum): # TODO: Move to domain? - VALUATION_IMPROVEMENT = "Valuation Improvement" - INCREASING_EPC = "Increasing EPC" - REDUCING_CO2_EMISSIONS = "Reducing CO2 emissions" - ENERGY_SAVINGS = "Energy Savings" - NONE = "None" - - class Portfolio(Base): __tablename__ = "portfolio" id = Column(Integer, primary_key=True, autoincrement=True) diff --git a/domain/modelling/portfolio_goal.py b/domain/modelling/portfolio_goal.py new file mode 100644 index 00000000..9785fd2e --- /dev/null +++ b/domain/modelling/portfolio_goal.py @@ -0,0 +1,23 @@ +"""PortfolioGoal — the retrofit objective a Scenario is scored against. + +Domain vocabulary (ubiquitous language): the goal a user sets for a Scenario — +raise the EPC band, cut CO₂, cut energy, or improve valuation. The enum +*values* are the canonical strings stored in the live ``scenario.goal`` / +``portfolio.goal`` columns and used by the front end; the Modelling stage's +Optimiser branches on them (#1160). + +Lives in ``domain/`` (not ``backend/``) so the domain, persistence +(``infrastructure/postgres/modelling``) and legacy app layers share one +definition — co-located with ``scenario.py``, which carries the goal. See +CONTEXT.md. +""" + +import enum + + +class PortfolioGoal(enum.Enum): + VALUATION_IMPROVEMENT = "Valuation Improvement" + INCREASING_EPC = "Increasing EPC" + REDUCING_CO2_EMISSIONS = "Reducing CO2 emissions" + ENERGY_SAVINGS = "Energy Savings" + NONE = "None"