refactor: conform standards docs to doc-forge wiki/lib layout; generic build.py; static kind links
This commit is contained in:
54
standards/src/wiki/01_overview.md
Normal file
54
standards/src/wiki/01_overview.md
Normal file
@@ -0,0 +1,54 @@
|
||||
# Overview
|
||||
|
||||
## Summary
|
||||
|
||||
The blueprint is a **hexagonal (ports and adapters) application** with a
|
||||
**spec-first** web edge and a **single composition root**. It favors explicit
|
||||
wiring over implicit magic, and it prefers existing internal libraries over
|
||||
hand-written infrastructure.
|
||||
|
||||
---
|
||||
|
||||
## The three layers
|
||||
|
||||
```
|
||||
interfaces/ ← HTTP edge: thin handlers bound to an OpenAPI operationId
|
||||
domain/ ← business core: models, ports (interfaces), services, orchestrators
|
||||
infra/ ← adapters: databases, caches, external clients, hardware
|
||||
```
|
||||
|
||||
Plus two optional layers:
|
||||
|
||||
```
|
||||
pipelines/ ← data-driven transformation graphs (extraction, enrichment)
|
||||
scripts/ ← operator scripts that reuse the composition root
|
||||
```
|
||||
|
||||
## Core principles
|
||||
|
||||
1. **One-way dependencies.** `interfaces -> domain -> infra` is allowed;
|
||||
the reverse is forbidden. The domain never imports a framework it does not
|
||||
own.
|
||||
2. **Ports in the domain, adapters in infra.** A service depends on an
|
||||
abstract port; the composition root injects a concrete adapter.
|
||||
3. **One composition root.** Nothing constructs its own dependencies. All
|
||||
wiring lives in one module (`init.py`) and is exposed as a flat resource
|
||||
registry.
|
||||
4. **Spec-first.** The HTTP contract is declared in an OpenAPI document. Route
|
||||
handlers are looked up by `operationId`. There are no decorator routers.
|
||||
5. **Reuse the libraries.** Pipeline orchestration, DI, Mongo repositories,
|
||||
ingestion, auth, and docs are provided by internal libraries. Do not
|
||||
reimplement them — see the library registry.
|
||||
6. **Explicit state machines.** Long-running work moves through explicit,
|
||||
durable statuses and can pause for human input.
|
||||
|
||||
## Runtime processes
|
||||
|
||||
A standard app may run as up to three processes that share the same
|
||||
composition root:
|
||||
|
||||
| Process | Entry point | Responsibility |
|
||||
|---|---|---|
|
||||
| API | `main.py` | serve HTTP; start/stop resources via lifespan |
|
||||
| Worker | `worker.py` | poll durable jobs, run pipelines, publish progress |
|
||||
| Scripts | `python -m scripts.<name>` | one-shot operator tasks |
|
||||
56
standards/src/wiki/10_testing.md
Normal file
56
standards/src/wiki/10_testing.md
Normal file
@@ -0,0 +1,56 @@
|
||||
# Testing
|
||||
|
||||
## Summary
|
||||
|
||||
Two test suites: **integration** (the HTTP edge with fakes) and **unit** (pure
|
||||
domain and pipeline logic). Both run under pytest with asyncio auto mode.
|
||||
|
||||
---
|
||||
|
||||
## Layout
|
||||
|
||||
```
|
||||
tests/
|
||||
integration/ # TestClient(app) + mocked resources
|
||||
unit/ # models, converters, pipeline stages/roots
|
||||
conftest.py # fixtures
|
||||
```
|
||||
|
||||
## Integration tests
|
||||
|
||||
- Build the app with `TestClient`.
|
||||
- Replace `app.state.resources` with fake repositories/services so no database
|
||||
is required.
|
||||
- Patch the auth introspection call (`httpx.AsyncClient.post`) to return a
|
||||
canned identity.
|
||||
- Assert status codes and bodies for list/get/create/update/delete, plus
|
||||
`400`, `404`, `204`, and the streaming endpoint's fallback.
|
||||
|
||||
```python
|
||||
client = TestClient(app)
|
||||
app.state.resources = {"item_svc": FakeItemService(), ...}
|
||||
|
||||
def test_create_item():
|
||||
r = client.post("/items", json={"name": "x"})
|
||||
assert r.status_code == 200
|
||||
```
|
||||
|
||||
## Unit tests
|
||||
|
||||
- Domain models and converters: pure, no I/O.
|
||||
- Pipeline stages: feed a fixture state, assert the transformed state.
|
||||
- Assembled pipeline roots: **fixture parity** — replay a stored fixture input
|
||||
and compare against a golden output file.
|
||||
|
||||
## Config-consistency tests
|
||||
|
||||
For rule-driven pipelines, add a test that cross-references rules against the
|
||||
declared pattern registry: every flag emitted must be defined; every defined
|
||||
flag must be reachable. This catches drift without running the pipeline.
|
||||
|
||||
## Conventions
|
||||
|
||||
- `asyncio_mode = "auto"`; no manual event loops.
|
||||
- Fixtures are checked into the repo (inputs + golden outputs).
|
||||
- Tests skip (not fail) when a required fixture is absent.
|
||||
- Do not mock the domain; mock only the infrastructure edge.
|
||||
82
standards/src/wiki/11_build_recipe.md
Normal file
82
standards/src/wiki/11_build_recipe.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Build Recipe
|
||||
|
||||
## Summary
|
||||
|
||||
Follow this ordered checklist to generate a new application of the standard
|
||||
shape. Every step is deterministic; only steps 2 and 7 require app-specific
|
||||
authoring.
|
||||
|
||||
---
|
||||
|
||||
## 0. Query the registry
|
||||
|
||||
Before writing anything, read the **library registry** and note which internal
|
||||
libraries provide DB, DI, routing, ingestion, auth, and docs. Do not
|
||||
reimplement them.
|
||||
|
||||
## 1. Scaffold the repository
|
||||
|
||||
Copy the `templates/` skeleton. It provides: `pyproject.toml`, `openapi.yaml`,
|
||||
`main.py`, `init.py`, `worker.py`, `config.py`, `Dockerfile`,
|
||||
`docker-compose.yaml`, `.env.example`, and the package tree
|
||||
(`domain/ infra/ interfaces/ pipelines/ tests/ scripts/`).
|
||||
|
||||
## 2. Author the spec *(app-specific)*
|
||||
|
||||
Write `openapi.yaml`: resources, schemas, `operationId`s, security, and any
|
||||
extension metadata. This is the contract.
|
||||
|
||||
## 3. Write each CRUD slice *(app-specific)*
|
||||
|
||||
For each resource, create the four-file slice (see **CRUD Template**):
|
||||
`models.py`, `ports/db.py`, `ports/service.py`, optional `converters.py`.
|
||||
|
||||
## 4. Wire the composition root
|
||||
|
||||
In `init.py`: build leaf repositories, then dependent repositories, then
|
||||
services; add infrastructure adaptors (cache store, progress bus, stores,
|
||||
fetchers). Add `get_svc` overloads.
|
||||
|
||||
## 5. Add routes
|
||||
|
||||
Create `interfaces/<resource>/routes.py` (thin handlers) and re-export them in
|
||||
`interfaces/openapi.py`. Add a cache decorator to read endpoints.
|
||||
|
||||
## 6. Bootstrap the app and worker
|
||||
|
||||
Confirm `main.py` wires the OpenAPI-first app with the lifespan. If the app has
|
||||
long-running jobs, adapt `worker.py` to poll the relevant job collection.
|
||||
|
||||
## 7. Add a pipeline family *(only if data-driven)*
|
||||
|
||||
Create `pipelines/<family>/` with `abc.py`, `impl.py`, `pipeline.yaml`,
|
||||
`resources/`, and register the root in `registry.py`. Add fixtures and a parity
|
||||
test.
|
||||
|
||||
## 8. Add infrastructure as needed
|
||||
|
||||
Only where the standard adaptors do not cover the need. Prefer library-backed
|
||||
adapters.
|
||||
|
||||
## 9. Tests
|
||||
|
||||
Add integration tests with fakes and unit tests for domain/pipeline logic; add
|
||||
a config-consistency test if using rule-driven pipelines.
|
||||
|
||||
## 10. Scripts and seeds
|
||||
|
||||
Add operator scripts under `scripts/` that use `init()` / `get_svc()` /
|
||||
`shutdown()`.
|
||||
|
||||
## 11. Docs
|
||||
|
||||
Author GSDFC docstrings, generate the type stubs (`.pyi`), mark the package
|
||||
`py.typed`, and build docs.
|
||||
|
||||
## Definition of done
|
||||
|
||||
- `python -c "import main"` succeeds.
|
||||
- `pytest` passes.
|
||||
- The app serves the spec's operations; the worker drains its queue.
|
||||
- No domain module imports a web framework.
|
||||
- No infrastructure was reimplemented that a library already provides.
|
||||
45
standards/src/wiki/index.md
Normal file
45
standards/src/wiki/index.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# The Aetos Standard App Blueprint
|
||||
|
||||
A single, reusable architecture for backend applications in the Aetos ecosystem.
|
||||
|
||||
Every application built with this blueprint has the same shape:
|
||||
|
||||
- a **spec-first HTTP layer** driven by an OpenAPI document;
|
||||
- a **domain layer** of pure models, ports, and services;
|
||||
- an **infrastructure layer** of adapters (MongoDB, cache, workers, LLM, mail);
|
||||
- a **composition root** that wires everything in exactly one place;
|
||||
- an optional **pipeline layer** for data-driven extraction/enrichment;
|
||||
- an optional **background worker** that runs long-running jobs and reports progress.
|
||||
|
||||
> This blueprints is deliberately **app-agnostic**. It never names a concrete
|
||||
> business domain. Any application — expenses, health metrics, inventory,
|
||||
> analytics — is produced by filling in **two axes only**: the resource model
|
||||
> and (if needed) the pipeline family.
|
||||
|
||||
## How to read this
|
||||
|
||||
| Page | What it covers |
|
||||
|---|---|
|
||||
| Overview | The philosophy and the one-way dependency rule |
|
||||
| Layering Rules | Which package may import which |
|
||||
| Composition Root | The single wiring point |
|
||||
| Spec-First Routing | OpenAPI as the source of truth |
|
||||
| CRUD Template | The canonical resource slice |
|
||||
| Pipeline Scaffold | Data-driven processing families |
|
||||
| Worker & Progress | Async jobs, status machine, streaming progress |
|
||||
| Human-in-the-Loop | Pausing a job to ask a question |
|
||||
| Infra Adapters | MongoDB, cache, stores, LLM, mail |
|
||||
| Testing | Integration + unit conventions |
|
||||
| Build Recipe | The ordered checklist to generate a new app |
|
||||
| Genericity Matrix | What is reusable vs what you must write |
|
||||
|
||||
## For AI agents
|
||||
|
||||
This content is served as MCP resources under `docs://index`, `docs://nav`,
|
||||
and `docs://modules/{module}`. To build a new app:
|
||||
|
||||
1. Read `build_recipe` first.
|
||||
2. Read `genericity_matrix` to know what you must author.
|
||||
3. Query the **library registry** (`find_library`) before writing any
|
||||
infrastructure yourself.
|
||||
4. Copy the `templates/` skeleton and fill the two app-specific axes.
|
||||
Reference in New Issue
Block a user