{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"\ud83e\udde9 Standards \u2014 the Aetos App Blueprint","text":"
One reusable architecture for every backend application in the Aetos ecosystem: a spec-first HTTP edge, a pure domain core, a pluggable infrastructure layer, and a single composition root.
Fill in two axes only and a new app is done: your resource model, and (if needed) your pipeline family. The blueprint itself never names a business domain \u2014 expenses, health metrics, inventory, and analytics are all the same shape underneath.
Doc model: this wiki is written for humans \u2014 how-to guides, the build recipe, and quality gates. The enforceable rules (layering, composition root, CRUD template, pipeline scaffold) and the library registry live in the reference pages, mirrored by the machine-readable standards MCP bundle. Query find_library before writing any infrastructure.
interfaces \u2192 domain \u2192 infra, enforcedReference (separate site, mirrors the MCP bundle): the enforceable rules \u2014 Layering, Composition Root, Spec-First Routing, CRUD Template, Pipeline Scaffold, Worker & Progress, Human-in-the-Loop, Infra Adapters, Genericity Matrix, Library Registry.
"},{"location":"#related-resources","title":"\ud83d\udd17 Related Resources","text":"standards server on :8009: read doc://index, doc://nav, doc://modules/{module}; call find_library(capability)libraries.json, served as registry://libraries)standards/src/templates/app/AGENTS.md-style build prompt from Build Recipe\u00a9 Aetoskia Internal \u2014 standards
The blueprint is a hexagonal (ports & adapters) application with a spec-first web edge and a single composition root. It favors explicit wiring over implicit magic and existing internal libraries over hand-written infrastructure. Start here to build the mental model; the enforceable details live in the reference pages.
"},{"location":"01_overview/#architecture","title":"\ud83c\udfd7\ufe0f Architecture","text":"flowchart LR\n subgraph EDGE[\"interfaces/ \u2014 HTTP edge\"]\n OAS[\"openapi.yaml<br/>operationId contract\"]\n RT[\"<resource>/routes.py<br/>thin handlers\"]\n end\n\n subgraph CORE[\"domain/ \u2014 pure business core\"]\n MD[\"models\"]\n PT[\"ports (abstract)\"]\n SV[\"services\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n RP[\"mongo repos (mongo-ops)\"]\n AD[\"cache \u00b7 stores \u00b7 llm \u00b7 mail\"]\n DB[(\"MongoDB\")]\n end\n\n CR[\"init.py<br/>composition root\"]\n\n OAS --> RT\n RT --> SV\n SV --> PT\n SV --> RP\n PT -. depends on .- RP\n CR --- RT\n CR --- SV\n CR --- RP\n RP --> DB\n AD --- CR The rule holds at every edge: code in an outer ring may depend on a ring closer to the core, never the reverse. domain/ never imports a web framework or a database driver.
Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter contract, resolved by the composition root at startup.
"},{"location":"01_overview/#the-one-way-dependency-rule","title":"\ud83d\udd04 The one-way dependency rule","text":"Ring May import Never importsinterfaces/ domain, framework glue (glue only) business logic, infra directly domain/ nothing but its own ports web frameworks, drivers, infra infra/ domain ports, drivers, libraries \u2014 init.py everything (the wiring point) \u2014"},{"location":"01_overview/#core-principles","title":"\ud83e\udded Core principles","text":"interfaces -> domain -> infra is allowed; the reverse is forbidden.init.py) exposed as a flat resource registry.openapi.yaml; route handlers are looked up by operationId. There are no decorator routers.A standard app may run as up to three processes that share the same composition root:
Process Entry point Responsibility APImain.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"},{"location":"01_overview/#read-next","title":"\u27a1\ufe0f Read Next","text":"What ships in every standard app, and how the pieces relate. This is the blueprint's component model \u2014 templates/app/ is a runnable instance of it.
%%{init: {\"flowchart\": {\"nodeSpacing\": 55, \"rankSpacing\": 120}}}%%\nflowchart TB\n subgraph EDGE[\"interfaces/ \u2014 HTTP edge\"]\n OAS[\"openapi.yaml\"]\n RU[\"resource routes\"]\n JU[\"job routes\"]\n end\n\n subgraph CORE[\"domain/ \u2014 business core\"]\n RI[\"<resource> \u00b7 models \u00b7 ports \u00b7 services\"]\n JI[\"<job/worker> \u00b7 models \u00b7 ports \u00b7 services\"]\n PI[\"pipelines/<family>\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n DB[(\"MongoDB repos\")]\n CA[\"redis cache\"]\n ST[\"stores \u00b7 progress bus\"]\n LL[\"llm clients\"]\n end\n\n W[\"worker.py\"]\n CR[\"init.py \u2014 composition root\"]\n\n OAS --> RU\n OAS --> JU\n RU --> RI\n JU --> JI\n JI --> PI\n PI --> DB\n PI --> LL\n RI --> CA\n RI --> DB\n JI --> ST\n W --> JI\n W --> PI\n\n CR === RU\n CR === JU\n CR === RI\n CR === JI\n CR === PI\n CR === W\n CR === DB\n CR === CA\n CR === ST Legend \u2014 solid arrows are direct calls; thick links are wires created by the composition root. Every component is injected, never self-constructed.
"},{"location":"02_components/#component-roles","title":"\ud83d\udce6 Component roles","text":""},{"location":"02_components/#1-interfaces-the-edge","title":"1.interfaces/ \u2014 the edge","text":"Thin handlers bound to openapi.yaml operationIds. They parse the request, call a service, and shape the response. No business rules, no I/O.
interfaces/openapi.py re-exports every route module so the OpenAPI-first app can resolve handlers.cache decorator on read endpoints.domain/ \u2014 the core","text":"Pure models, ports (abstract contracts), and services (use cases). No framework imports, no drivers.
<aggregate>/models.py \u2014 dataclasses, enums, document shapes.<aggregate>/ports/db.py \u2014 repo contract; <aggregate>/ports/service.py \u2014 service contract.infra/ \u2014 the adapters","text":"Concrete implementations of the domain ports plus cross-cutting infrastructure:
db/ \u2014 mongo-ops repositories and the connection lifecycle.adapters/ \u2014 cache store, progress bus, storages, fetchers (library-backed).pipelines/ \u2014 data-driven families (optional)","text":"Extraction and enrichment families, each a deterministic step chain (abc.py / impl.py / pipeline.yaml / resources/). Roots are registered in the pattern registry and driven either by a worker job or a CLI.
worker.py \u2014 the job worker (optional)","text":"A separate process that polls durable job collections, runs pipeline roots, and publishes progress to the progress bus. It shares the same composition root as the API.
"},{"location":"02_components/#6-initpy-the-composition-root","title":"6.init.py \u2014 the composition root","text":"The only place anything is constructed: leaf repositories \u2192 dependent repositories \u2192 services \u2192 adapters. Exposes get_svc overloads and starts / stops resources on the app lifespan.
sequenceDiagram\n participant C as Client\n participant R as route (interfaces)\n participant S as service (domain)\n participant P as repo (infra)\n participant DB as MongoDB\n\n C->>R: POST /items (operationId)\n R->>S: svc.create(payload)\n S->>P: repo.insert(doc)\n P->>DB: insert\n DB-->>P: ack\n P-->>S: doc\n S-->>R: result\n R-->>C: 200 JSON"},{"location":"02_components/#related","title":"Related","text":"The ordered, deterministic checklist for generating a new application of the standard shape. Every step is fixed; only steps 2 and 3 need app-specific authoring. Give an AI agent the copyable prompt below (or read it from the standards MCP bundle as doc://modules/build_recipe) and it can produce a complete app in one pass.
# Copy the runnable skeleton (from the standards docs checkout)\ncp -r <docs>/standards/src/templates/app apps/<your-app>\ncd apps/<your-app>\n Then follow the numbered steps in the prompt.
"},{"location":"03_build_recipe/#agent-prompt-copyable","title":"\ud83d\udccb Agent prompt (copyable)","text":"# Build a new application from the Aetos Standards blueprint\n\nYou are coding a complete new backend application conforming to the Aetos\nstandard app blueprint. Execute every step in order. Only steps 2 and 3 use the\napp-specific details provided by the user; everything else is deterministic.\n\nBefore writing any code, connect to the `standards` MCP server and read:\n- `doc://nav` (the module list)\n- `doc://modules/layering_rules` (the dependency rules)\n- `doc://modules/genericity_matrix` (what is generic vs app-specific)\nThen call `find_library` with capabilities like `db`, `di`, `routing`,\n`ingestion`, `auth`, `docs`, `pipeline`, and note the libraries that already\nexist. Never reimplement one.\n\n1. **Scaffold** \u2014 copy `src/templates/app/` into `apps/<your-app>/`. Keep the\n package tree: `domain/`, `infra/`, `interfaces/`, `pipelines/`, `tests/`,\n `scripts/`, plus `pyproject.toml`, `openapi.yaml`, `config.py`, `init.py`,\n `main.py`, `worker.py`, `Dockerfile`, `docker-compose.yaml`, `.env.example`.\n\n2. **Author the spec (app-specific)** \u2014 write `openapi.yaml`: resources,\n schemas, `operationId`s, security, and extension metadata. This is the\n contract and drives all routing.\n\n3. **Write each CRUD slice (app-specific)** \u2014 per resource create the\n four-file slice: `models.py`, `ports/db.py`, `ports/service.py`, and\n optional `converters.py`. Follow the CRUD template.\n\n4. **Wire the composition root** \u2014 in `init.py`, build leaf repositories, then\n dependent repositories, then services; add infra adapters (db, cache,\n stores, progress bus, fetchers). Expose `get_svc` overloads.\n\n5. **Add routes** \u2014 create `interfaces/<resource>/routes.py` thin handlers\n bound to `operationId`s and re-export them in `interfaces/openapi.py`. Add\n the cache decorator to read endpoints.\n\n6. **Bootstrap app and worker** \u2014 confirm `main.py` runs the OpenAPI-first app\n with its lifespan. If the app has long-running jobs, adapt `worker.py` to\n poll the job collection, run the pipeline, and publish progress.\n\n7. **Add pipeline families (only if data-driven)** \u2014 create\n `pipelines/<family>/` with `abc.py`, `impl.py`, `pipeline.yaml`,\n `resources/`, and register the root. Add fixtures and a parity test.\n\n8. **Add infrastructure only where needed** \u2014 prefer a library-backed adapter.\n Never hand-write what an internal library already provides.\n\n9. **Tests** \u2014 integration tests with fakes at the HTTP edge; unit tests for\n domain and pipeline logic; a config-consistency test for rule-driven\n pipelines.\n\n10. **Scripts and seeds** \u2014 operator scripts under `scripts/` using\n `init()` / `get_svc()` / `shutdown()`.\n\n11. **Docs** \u2014 author GSDFC docstrings, generate `.pyi` stubs, mark the package\n `py.typed`, and build the docs.\n\n**Definition of done:**\n- `python -c \"import main\"` succeeds.\n- `pytest` passes.\n- The app serves every operation in the spec; the worker drains its queue.\n- No domain module imports a web framework.\n- No infrastructure was reimplemented that a library already provides.\n"},{"location":"03_build_recipe/#where-the-steps-land","title":"\ud83e\udded Where the steps land","text":"The steps map onto the layers as follows:
Step Package / file Depends on 1 \u00b7 Scaffold wholetemplates/app/ \u2014 2 \u00b7 Spec openapi.yaml \u2014 3 \u00b7 CRUD slices domain/<resource>/ spec, CRUD template 4 \u00b7 Wiring init.py every slice 5 \u00b7 Routes interfaces/<resource>/ services, openapi route registry 6 \u00b7 Bootstrap main.py, worker.py composition root 7 \u00b7 Pipelines pipelines/<family>/ library pipeline engine 8 \u00b7 Infra infra/ domain ports 9 \u00b7 Tests tests/ app + fixtures 10 \u00b7 Scripts scripts/ composition root 11 \u00b7 Docs docstrings + docs build code, Doc Forge"},{"location":"03_build_recipe/#definition-of-done","title":"\u26a0\ufe0f Definition of done","text":"python -c \"import main\" succeeds.pytest passes.Two suites run under pytest with asyncio auto mode: integration (the HTTP edge against fakes) and unit (pure domain and pipeline logic). Finish a blueprint app only when every gate below passes.
"},{"location":"04_development/01_testing/#layout","title":"\ud83d\uddc2\ufe0f Layout","text":"tests/\n integration/ # TestClient(app) + mocked resources\n unit/ # models, converters, pipeline stages/roots\n conftest.py # fixtures\n"},{"location":"04_development/01_testing/#integration-tests","title":"\ud83d\ude80 Integration tests","text":"TestClient.app.state.resources with fake repositories/services so no database is required.httpx.AsyncClient.post) to return a canned identity.400, 404, 204, and the streaming endpoint's fallback.client = TestClient(app)\napp.state.resources = {\"item_svc\": FakeItemService(), ...}\n\ndef test_create_item():\n r = client.post(\"/items\", json={\"name\": \"x\"})\n assert r.status_code == 200\n"},{"location":"04_development/01_testing/#unit-tests","title":"\ud83e\uddea Unit tests","text":"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.
"},{"location":"04_development/01_testing/#quality-gates","title":"\u2705 Quality gates","text":"A definition of done for any standard app:
python -c \"import main\" succeeds.pytest passes.asyncio_mode = \"auto\"; no manual event loops.