# Infra Adapters ## Summary All external systems are reached through adapters in `infra/`. Prefer an existing internal library over writing a new adapter; where no library exists, copy the Tier B scaffold from `templates/app`. See the Genericity Matrix for the Tier A / B / C split. --- ## Database (MongoDB) - A single connection manager owns the client lifecycle. `setup_database(uri, db_name)` / `shutdown_database()`. - A repository **factory** selects the repository flavour: ```python repo_factory(collection, document, engine=None, populate_rules=None, cache=None) ``` - base repository — plain CRUD; - populating repository — resolves referenced documents (foreign keys); - cached repository — an LRU/TTL cache in front of reads. - Referenced documents are described declaratively: ```python PopulateRule(field="owner", collection="users", nested_rules=[], max_depth=2, projection=None) ``` - Transactions use a session context manager provided by the library. **Tier A — Library (Mongo Ops).** Do not write raw Motor calls in the domain; consult the registry and the library's MCP before touching the driver. ## HTTP response cache **Tier B — template.** Copy from `templates/app`; not a standalone library. A generic GET cache sits behind a decorator. ```python @cache_response(ttl=60) async def list_items(request): ... ``` - Keyed on path + query + user. - No-op when no store is wired. - `invalidate(*resources)` bumps a generation counter for write-triggered invalidation. ## Event / progress bus **Tier B — template.** Copy from `templates/app`. A port with an in-memory default adapter (`publish`, `get_events`). Swap for a durable broker in production (see Worker & Progress). ## External clients (ingestion) External fetchers (files, mail, HTTP) are adapters that wrap ingestion libraries. Construct them in the composition root and inject into orchestrators. **Tier A — Libraries:** Omniread (formats) and Mail Intake (email). ## LLM clients **Tier B — template.** Copy the pool scaffold from `templates/app`. When a pipeline calls a model provider, route through a pool adaptor that handles provider selection, retries, and client lifecycle: ```python pool = setup_async_pool(provider=settings.llm_provider) result = await pool.complete(model="...", messages=[...]) ``` Providers are selected by `LLM_PROVIDER` and configured by environment. ## Key-value store **Tier B — template.** Copy from `templates/app`. For report/snapshot storage, provide a pluggable store port with a Redis adapter and an in-memory fallback. ## Rule - **Tier A (libraries: DB, DI, ingestion, mail, auth, docs, pipelines)** — use the library; writing a private copy is a defect. Query `find_library` first. - **Tier B (cache, progress bus, LLM pool, KV store, composition shape, worker, tests)** — these are template code, not libraries: copy them from `templates/app` rather than searching the registry. - **Tier C (models, business logic, pipeline families, rules)** — author them yourself.