{"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 framework-free 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 Build Recipe, 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 docs://index, docs://nav, docs://modules/{module}; call find_library(capability)libraries.json, served as registry://libraries)standards/src/templates/app/start_app prompt on the standards server, or read the Start an App guide\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 business core\"]\n MD[\"models\"]\n PT[\"ports (adapter binding seam)\"]\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; only its ports/ seam reaches into infra/ to bind adapters.
Legend \u2014 solid arrows are compile-time calls; dotted arrows are the port/adapter binding, performed 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/ its own models + its ports/ seam (which binds infra/libraries) web frameworks, interfaces/ 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.
The blueprint has two orthogonal views: runtime flow (who calls whom) and composition-root wiring (who constructs whom). Keeping them separate makes the layering visible instead of hiding it behind a hub.
"},{"location":"02_components/#runtime-flow","title":"\ud83d\udd00 Runtime flow","text":"Who calls whom at request and job time:
%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n subgraph REQ[\"Request path \u2014 API process\"]\n direction LR\n CL([\"Client<br/>operationId\"]) --> RT[\"interfaces/<resource>/routes.py\"]\n RT --> SVC[\"<resource>_svc\"]\n SVC --> REPO[\"<resource>_repo\"]\n end\n\n subgraph WRK[\"Background path \u2014 worker process\"]\n direction LR\n W[\"worker.py\"] --> JSVC[\"job_svc\"]\n W --> PIPE[\"pipelines/<family>\"]\n PIPE --> ISVC[\"item_svc\"]\n JSVC --> JREPO[\"job_repo\"]\n ISVC --> IREPO[\"item_repo\"]\n end\n\n subgraph INFRA[\"infra/ \u2014 adapters\"]\n MONGO[(\"MongoDB / mongo_ops\")]\n end\n\n REPO --> MONGO\n JREPO --> MONGO\n IREPO --> MONGO Legend \u2014 solid arrows are runtime calls. Nothing is self-constructed.
"},{"location":"02_components/#composition-root-wiring","title":"\ud83e\uddf5 Composition-root wiring","text":"Who constructs what at startup:
%%{init: {\"flowchart\": {\"nodeSpacing\": 50, \"rankSpacing\": 80}}}%%\nflowchart TB\n INIT[\"init.py \u2014 composition root\"]\n SETUP[\"infra/db/setup.py\"]\n FACT[\"domain/*/ports/*<br/>factory seams\"]\n REPOS[\"repos<br/>infra/db/repos/mongo.py\"]\n SVCES[\"services<br/>CrudsServiceAdaptor\"]\n STATE[\"app.state.db_resources\"]\n HEXA[\"hexa.build\"]\n RT[\"routes.py\"]\n WK[\"worker.py\"]\n\n INIT --> SETUP\n INIT --> FACT\n FACT --> REPOS\n FACT --> SVCES\n REPOS --> SVCES\n HEXA -.-> SVCES\n INIT --> STATE\n STATE --> RT\n INIT --> WK Legend \u2014 init.py is the only place that imports concrete adapters. The ports/ factory seams bind infra; the API reads resources from app.state.db_resources, the worker calls init() directly.
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":"Models, ports, and services (use cases). No framework imports. The ports/ package is the one seam allowed to reach into infra/: each port module declares the document shape and a factory that binds the adapter.
<aggregate>/models.py \u2014 dataclasses, enums, document shapes.<aggregate>/ports/db.py \u2014 document model + create_<aggregate>_repo().<aggregate>/ports/service.py \u2014 service + get_<aggregate>_service(repo).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 (port factory / adapter)\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":"This is the entry point for building a new application from the blueprint. The goal is one deterministic pass: you supply only the two axes, and the agent does the rest by reading the standards MCP, importing Tier A libraries, and copying Tier B template components.
Doc model: this page is the human entry point. The deterministic, machine-facing checklist lives in the reference page Build Recipe, served as docs://modules/build_recipe.
Gather these before starting \u2014 the start_app MCP prompt asks for them:
openapi.yaml if you already have it.py-jwt primitives only), and an authless app is just a spec with no security keys \u2014 see Spec-First Routing \u2192 Authless apps.If you can state axes 1 and 2, the rest is mechanical.
"},{"location":"03_start_an_app/#how-to-start","title":"\u25b6\ufe0f How to start","text":"From an MCP client connected to the standards server:
intake prompt (or start_app with no arguments) to get the intake questions.build (build(app_name=..., resources=..., pipelines=..., auth=...)), or use start_app to intake-and-build in one call.docs://modules/build_recipe; the agent scaffolds with get_app_template() and pulls exact files with get_app_component(kind, resource=..., family=...), authoring only the app-specific parts.trial (trial(app_name=..., build_path=...)) to build into a scratch directory, prove it builds, and report findings.Manual alternative \u2014 copy the skeleton and follow the reference recipe:
cp -r <docs>/standards/src/templates/app apps/<your-app>\n"},{"location":"03_start_an_app/#reference","title":"\ud83d\udcda Reference","text":"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.db_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.db_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.