docs(claude): record the deep-module principle for future development

This commit is contained in:
2026-08-19 15:01:10 +03:30
parent 9858e27c2d
commit 07b50d6987

View File

@@ -113,6 +113,27 @@ MinIO/Qdrant/SQLAlchemy client-construction code. Use ports only for
external side effects/persistence — not around pure local functions.
(ADR-0015)
### Prefer deep modules over shallow ones
When a package exposes several small pure functions that a caller must
compose correctly every time (right dispatch, right order, right
thread/async offload), give it one entry point that owns that composition,
and keep the small functions internal — exported only where their own unit
tests need them. A shallow interface (one whose surface is nearly as complex
as its implementation) pushes a correctness obligation onto every call site;
a deep one absorbs it once. Apply the deletion test when unsure: if deleting
the wrapper would concentrate the composition logic back into every caller
rather than just relocate it, the wrapper is worth having.
Worked example: `src/application/ingestion/` exposes `parse_and_chunk_document`
as its only caller-facing entry point. It dispatches on source type and owns
the `anyio.to_thread.run_sync` + `CapacityLimiter` offload ADR-0017 requires;
`parse_docx`/`parse_csv`/`parse_xlsx`/`chunk_document` stay in the package,
exported mainly for their own tests, not for outside callers to reach for
directly. Follow this pattern in `application/` as new packages are added
there — `points/`, `retrieval/`, `threads/` — rather than exposing their
internals as the primary surface.
### Resource lifetime rules (ADR-0012)
- Application-lifetime objects (SQLAlchemy engine/sessionmaker, Qdrant client,