{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"The Aetos Standard App Blueprint","text":"
A single, reusable architecture for backend applications in the Aetos ecosystem.
Every application built with this blueprint has the same shape:
This blueprints is deliberately app-agnostic. It never names a concrete business domain. Any application \u2014 expenses, health metrics, inventory, analytics \u2014 is produced by filling in two axes only: the resource model and (if needed) the pipeline family.
"},{"location":"#how-to-read-this","title":"How to read this","text":"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"},{"location":"#for-ai-agents","title":"For AI agents","text":"This content is served as MCP resources under docs://index, docs://nav, and docs://modules/{module}. To build a new app:
build_recipe first.genericity_matrix to know what you must author.find_library) before writing any infrastructure yourself.templates/ skeleton and fill the two app-specific axes.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.
"},{"location":"01_overview/#the-three-layers","title":"The three layers","text":"interfaces/ \u2190 HTTP edge: thin handlers bound to an OpenAPI operationId\ndomain/ \u2190 business core: models, ports (interfaces), services, orchestrators\ninfra/ \u2190 adapters: databases, caches, external clients, hardware\n Plus two optional layers:
pipelines/ \u2190 data-driven transformation graphs (extraction, enrichment)\nscripts/ \u2190 operator scripts that reuse the composition root\n"},{"location":"01_overview/#core-principles","title":"Core principles","text":"interfaces -> domain -> infra is allowed; the reverse is forbidden. The domain never imports a framework it does not own.init.py) and is exposed as a flat resource registry.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":"10_testing/","title":"Testing","text":""},{"location":"10_testing/#summary","title":"Summary","text":"Two test suites: integration (the HTTP edge with fakes) and unit (pure domain and pipeline logic). Both run under pytest with asyncio auto mode.
"},{"location":"10_testing/#layout","title":"Layout","text":"tests/\n integration/ # TestClient(app) + mocked resources\n unit/ # models, converters, pipeline stages/roots\n conftest.py # fixtures\n"},{"location":"10_testing/#integration-tests","title":"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":"10_testing/#unit-tests","title":"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":"10_testing/#conventions","title":"Conventions","text":"asyncio_mode = \"auto\"; no manual event loops.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.
"},{"location":"11_build_recipe/#0-query-the-registry","title":"0. Query the registry","text":"Before writing anything, read the library registry and note which internal libraries provide DB, DI, routing, ingestion, auth, and docs. Do not reimplement them.
"},{"location":"11_build_recipe/#1-scaffold-the-repository","title":"1. Scaffold the repository","text":"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/).
Write openapi.yaml: resources, schemas, operationIds, security, and any extension metadata. This is the contract.
For each resource, create the four-file slice (see CRUD Template): models.py, ports/db.py, ports/service.py, optional converters.py.
In init.py: build leaf repositories, then dependent repositories, then services; add infrastructure adaptors (cache store, progress bus, stores, fetchers). Add get_svc overloads.
Create interfaces/<resource>/routes.py (thin handlers) and re-export them in interfaces/openapi.py. Add a cache decorator to read endpoints.
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.
Create pipelines/<family>/ with abc.py, impl.py, pipeline.yaml, resources/, and register the root in registry.py. Add fixtures and a parity test.
Only where the standard adaptors do not cover the need. Prefer library-backed adapters.
"},{"location":"11_build_recipe/#9-tests","title":"9. Tests","text":"Add integration tests with fakes and unit tests for domain/pipeline logic; add a config-consistency test if using rule-driven pipelines.
"},{"location":"11_build_recipe/#10-scripts-and-seeds","title":"10. Scripts and seeds","text":"Add operator scripts under scripts/ that use init() / get_svc() / shutdown().
Author GSDFC docstrings, generate the type stubs (.pyi), mark the package py.typed, and build docs.
python -c \"import main\" succeeds.pytest passes.