From 5470fa1d93e00e28aac299c0c65692c75ce53ca6 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 1 Jun 2026 15:46:46 +0000 Subject: [PATCH] move landlord overrides --- .../landlord_description_overrides/handler.py | 6 +++--- ...ython-writes-landlord-overrides-directly.md | 18 ++++++++++-------- infrastructure/landlord_overrides/__init__.py | 0 .../landlord_overrides_postgres_repository.py | 6 +++++- .../landlord_override_repository.py | 2 +- ...t_landlord_overrides_postgres_repository.py | 4 ++-- 6 files changed, 21 insertions(+), 15 deletions(-) create mode 100644 infrastructure/landlord_overrides/__init__.py rename infrastructure/{postgres => landlord_overrides}/landlord_overrides_postgres_repository.py (91%) diff --git a/applications/landlord_description_overrides/handler.py b/applications/landlord_description_overrides/handler.py index b5d22620..9637d16c 100644 --- a/applications/landlord_description_overrides/handler.py +++ b/applications/landlord_description_overrides/handler.py @@ -16,14 +16,14 @@ from domain.epc.wall_type_construction_dates import ( ) from infrastructure.chatgpt.chatgpt import ChatGPT from infrastructure.chatgpt.chatgpt_column_classifier import ChatGptColumnClassifier +from infrastructure.landlord_overrides.landlord_overrides_postgres_repository import ( + LandlordOverridesRepository, +) from infrastructure.postgres.config import PostgresConfig from infrastructure.postgres.engine import commit_scope, make_engine, make_session from infrastructure.postgres.landlord_built_form_type_override_table import ( LandlordBuiltFormTypeOverrideRow, ) -from infrastructure.postgres.landlord_overrides_postgres_repository import ( - LandlordOverridesRepository, -) from infrastructure.postgres.landlord_property_type_override_table import ( LandlordPropertyTypeOverrideRow, ) diff --git a/docs/adr/0003-python-writes-landlord-overrides-directly.md b/docs/adr/0003-python-writes-landlord-overrides-directly.md index 84f9065a..b199072b 100644 --- a/docs/adr/0003-python-writes-landlord-overrides-directly.md +++ b/docs/adr/0003-python-writes-landlord-overrides-directly.md @@ -53,24 +53,26 @@ The `WHERE existing.source = 'classifier'` guard is load-bearing: it lets the cl This ADR also fixes a placement convention for Postgres adapters going forward. The codebase currently has the ChatGPT classifier split cleanly along DDD lines — port in `domain/`, adapter in `infrastructure/chatgpt/` — but the `tasks` Postgres adapter does not follow the same shape: its concrete class lives in `repositories/tasks/`, not `infrastructure/postgres/`. -The convention going forward is: +The convention going forward separates the persistence *behaviour* (grouped by aggregate) from the schema *mirrors* (grouped by technology, since they share pgEnums and engine metadata): - **Port (protocol / abstract base):** `repositories//_repository.py` -- **Postgres adapter (concrete):** `infrastructure/postgres/_postgres_repository.py` -- **SQLModel row class:** `infrastructure/postgres/_table.py` +- **Postgres repository adapter (concrete):** `infrastructure//_postgres_repository.py` +- **SQLModel row class (`table=True` schema mirror):** `infrastructure/postgres/_table.py` -The `LandlordOverridesRepository` adapter follows this convention: the concrete class lives at `infrastructure/postgres/landlord_overrides_postgres_repository.py`, with one `…_table.py` per category alongside it. The `…Row` classes stay one-per-table — each mirrors a genuinely distinct Drizzle table and `value` pgEnum, so they are schema mirrors, not duplicated logic. +The `LandlordOverridesRepository` adapter follows this convention: the concrete class — the aggregate's "talker" to Postgres — lives at `infrastructure/landlord_overrides/landlord_overrides_postgres_repository.py`, while the per-category `…Row` classes stay in `infrastructure/postgres/`. The `…Row` classes are one-per-table — each mirrors a genuinely distinct Drizzle table and `value` pgEnum, and they share the single `override_source` pgEnum instance, so they belong together in the Postgres technology bucket as schema mirrors, not duplicated logic. + +(This refines the placement first sketched in this ADR, which put the adapter in `infrastructure/postgres/` alongside the row classes. The adapter holds no schema — only the write path — so it groups by aggregate; only the `table=True` mirrors stay tech-bucketed.) **Existing outliers to relocate in a follow-up:** -- `repositories/tasks/task_postgres_repository.py` → `infrastructure/postgres/task_postgres_repository.py` -- `repositories/tasks/subtask_postgres_repository.py` → `infrastructure/postgres/subtask_postgres_repository.py` +- `repositories/tasks/task_postgres_repository.py` → `infrastructure/tasks/task_postgres_repository.py` +- `repositories/tasks/subtask_postgres_repository.py` → `infrastructure/tasks/subtask_postgres_repository.py` -Both moves are mechanical (import-path updates only). They are intentionally out of scope for the present PR. +(Their `task_table.py` / `subtask_table.py` schema mirrors already sit correctly in `infrastructure/postgres/`.) Both moves are mechanical (import-path updates only). They are intentionally out of scope for the present PR. ## Out of scope (deferred to follow-up work) -- Relocating `task_postgres_repository.py` and `subtask_postgres_repository.py` into `infrastructure/postgres/` per the convention above. +- Relocating `task_postgres_repository.py` and `subtask_postgres_repository.py` into `infrastructure/tasks/` per the convention above. - ~~Extracting a shared upsert helper / base class once a third `landlord_*_overrides` column lands — until then the per-category adapters' 95%-identical bodies are kept side-by-side for direct comparison.~~ **Done.** The per-category adapter bodies were byte-identical (varying only in their row class), so they were consolidated into one generic `LandlordOverridesRepository[E]` parameterised by row class rather than waiting for a third column. - Switching `applications/landlord_description_overrides/handler.py` to acquire its `Session` via a `@subtask_handler()`-style decorator instead of building its own engine. - A cross-repo PR amending ADR-0002 to point at this ADR. diff --git a/infrastructure/landlord_overrides/__init__.py b/infrastructure/landlord_overrides/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/infrastructure/postgres/landlord_overrides_postgres_repository.py b/infrastructure/landlord_overrides/landlord_overrides_postgres_repository.py similarity index 91% rename from infrastructure/postgres/landlord_overrides_postgres_repository.py rename to infrastructure/landlord_overrides/landlord_overrides_postgres_repository.py index d561a492..5be7431c 100644 --- a/infrastructure/postgres/landlord_overrides_postgres_repository.py +++ b/infrastructure/landlord_overrides/landlord_overrides_postgres_repository.py @@ -11,7 +11,11 @@ category enum ``E`` and pass the matching ``…Row`` class: LandlordOverridesRepository[PropertyType](session, LandlordPropertyTypeOverrideRow) -Per ADR-0003 §File layout, Postgres adapters live in ``infrastructure/postgres/``. +This adapter -- the aggregate's "talker" to Postgres -- lives under +``infrastructure/landlord_overrides/``, grouped by aggregate. The ``table=True`` +SQLModel row classes it writes through stay in ``infrastructure/postgres/`` as +schema mirrors (they share the ``override_source`` pgEnum and register against +the same engine metadata). See ADR-0003 §File layout. """ from __future__ import annotations diff --git a/repositories/landlord_overrides/landlord_override_repository.py b/repositories/landlord_overrides/landlord_override_repository.py index df1c55a7..ef1fed9d 100644 --- a/repositories/landlord_overrides/landlord_override_repository.py +++ b/repositories/landlord_overrides/landlord_override_repository.py @@ -16,7 +16,7 @@ class LandlordOverrideRepository(ABC, Generic[E]): interface and never names a concrete table. A single concrete adapter, - ``infrastructure/postgres/landlord_overrides_postgres_repository.LandlordOverridesRepository``, + ``infrastructure/landlord_overrides/landlord_overrides_postgres_repository.LandlordOverridesRepository``, serves every category (see ADR-0003) -- it is parameterised by the SQLModel row class for the target table. """ diff --git a/tests/repositories/landlord_overrides/postgres/test_landlord_overrides_postgres_repository.py b/tests/repositories/landlord_overrides/postgres/test_landlord_overrides_postgres_repository.py index 31b9df33..1ce4e997 100644 --- a/tests/repositories/landlord_overrides/postgres/test_landlord_overrides_postgres_repository.py +++ b/tests/repositories/landlord_overrides/postgres/test_landlord_overrides_postgres_repository.py @@ -27,10 +27,10 @@ from sqlmodel import Session, SQLModel, select from domain.epc.property_type import PropertyType from domain.epc.wall_type import WallType -from infrastructure.postgres.landlord_override_enums import OverrideSource -from infrastructure.postgres.landlord_overrides_postgres_repository import ( +from infrastructure.landlord_overrides.landlord_overrides_postgres_repository import ( LandlordOverridesRepository, ) +from infrastructure.postgres.landlord_override_enums import OverrideSource from infrastructure.postgres.landlord_property_type_override_table import ( LandlordPropertyTypeOverrideRow, )