{"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:

  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.
"},{"location":"01_overview/","title":"Overview","text":""},{"location":"01_overview/#summary","title":"Summary","text":"

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":"
  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 \u2014 see the library registry.
  6. Explicit state machines. Long-running work moves through explicit, durable statuses and can pause for human input.
"},{"location":"01_overview/#runtime-processes","title":"Runtime processes","text":"

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"},{"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":"
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":""},{"location":"10_testing/#config-consistency-tests","title":"Config-consistency 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":""},{"location":"11_build_recipe/","title":"Build Recipe","text":""},{"location":"11_build_recipe/#summary","title":"Summary","text":"

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/).

"},{"location":"11_build_recipe/#2-author-the-spec-app-specific","title":"2. Author the spec (app-specific)","text":"

Write openapi.yaml: resources, schemas, operationIds, security, and any extension metadata. This is the contract.

"},{"location":"11_build_recipe/#3-write-each-crud-slice-app-specific","title":"3. Write each CRUD slice (app-specific)","text":"

For each resource, create the four-file slice (see CRUD Template): models.py, ports/db.py, ports/service.py, optional converters.py.

"},{"location":"11_build_recipe/#4-wire-the-composition-root","title":"4. Wire the composition root","text":"

In init.py: build leaf repositories, then dependent repositories, then services; add infrastructure adaptors (cache store, progress bus, stores, fetchers). Add get_svc overloads.

"},{"location":"11_build_recipe/#5-add-routes","title":"5. Add routes","text":"

Create interfaces/<resource>/routes.py (thin handlers) and re-export them in interfaces/openapi.py. Add a cache decorator to read endpoints.

"},{"location":"11_build_recipe/#6-bootstrap-the-app-and-worker","title":"6. Bootstrap the app and worker","text":"

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.

"},{"location":"11_build_recipe/#7-add-a-pipeline-family-only-if-data-driven","title":"7. Add a pipeline family (only if data-driven)","text":"

Create pipelines/<family>/ with abc.py, impl.py, pipeline.yaml, resources/, and register the root in registry.py. Add fixtures and a parity test.

"},{"location":"11_build_recipe/#8-add-infrastructure-as-needed","title":"8. Add infrastructure as needed","text":"

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().

"},{"location":"11_build_recipe/#11-docs","title":"11. Docs","text":"

Author GSDFC docstrings, generate the type stubs (.pyi), mark the package py.typed, and build docs.

"},{"location":"11_build_recipe/#definition-of-done","title":"Definition of done","text":""}]}