Files
docs/standards/src/lib/09_infra_adaptors.md

100 lines
3.0 KiB
Markdown

# 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.