All checks were successful
continuous-integration/drone/push Build is passing
New wiki page documents the ten per-repo MCP servers (standards + libraries, ports 8001-8010) and per-client config for Claude Code, Codex, Antigravity, OpenCode, and PyCharm, each with linked localhost / mcp.aetoskia.com tabs.
952 lines
25 KiB
HTML
952 lines
25 KiB
HTML
|
||
<!doctype html>
|
||
<html lang="en" class="no-js">
|
||
<head>
|
||
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||
|
||
<meta name="description" content="The philosophy and prompts behind the standardized application blueprint.">
|
||
|
||
|
||
|
||
|
||
<link rel="prev" href="../01_overview/">
|
||
|
||
|
||
<link rel="next" href="../03_start_an_app/">
|
||
|
||
|
||
<link rel="icon" href="../assets/images/favicon.png">
|
||
<meta name="generator" content="mkdocs-1.6.1, mkdocs-material-9.6.23">
|
||
|
||
|
||
|
||
<title>App Components - Aetos Standards — Wiki</title>
|
||
|
||
|
||
|
||
<link rel="stylesheet" href="../assets/stylesheets/main.84d31ad4.min.css">
|
||
|
||
|
||
<link rel="stylesheet" href="../assets/stylesheets/palette.06af60db.min.css">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i%7CJetBrains+Mono:400,400i,700,700i&display=fallback">
|
||
<style>:root{--md-text-font:"Inter";--md-code-font:"JetBrains Mono"}</style>
|
||
|
||
|
||
|
||
<script>__md_scope=new URL("..",location),__md_hash=e=>[...e].reduce(((e,_)=>(e<<5)-e+_.charCodeAt(0)),0),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<body dir="ltr" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="cyan">
|
||
|
||
|
||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off">
|
||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off">
|
||
<label class="md-overlay" for="__drawer"></label>
|
||
<div data-md-component="skip">
|
||
|
||
|
||
<a href="#app-components" class="md-skip">
|
||
Skip to content
|
||
</a>
|
||
|
||
</div>
|
||
<div data-md-component="announce">
|
||
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<header class="md-header" data-md-component="header">
|
||
<nav class="md-header__inner md-grid" aria-label="Header">
|
||
<a href=".." title="Aetos Standards — Wiki" class="md-header__button md-logo" aria-label="Aetos Standards — Wiki" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||
|
||
</a>
|
||
<label class="md-header__button md-icon" for="__drawer">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3zm0 5h18v2H3zm0 5h18v2H3z"/></svg>
|
||
</label>
|
||
<div class="md-header__title" data-md-component="header-title">
|
||
<div class="md-header__ellipsis">
|
||
<div class="md-header__topic">
|
||
<span class="md-ellipsis">
|
||
Aetos Standards — Wiki
|
||
</span>
|
||
</div>
|
||
<div class="md-header__topic" data-md-component="header-topic">
|
||
<span class="md-ellipsis">
|
||
|
||
App Components
|
||
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
<form class="md-header__option" data-md-component="palette">
|
||
|
||
|
||
|
||
|
||
<input class="md-option" data-md-color-media="" data-md-color-scheme="slate" data-md-color-primary="deep-purple" data-md-color-accent="cyan" aria-hidden="true" type="radio" name="__palette" id="__palette_0">
|
||
|
||
|
||
</form>
|
||
|
||
|
||
|
||
<script>var palette=__md_get("__palette");if(palette&&palette.color){if("(prefers-color-scheme)"===palette.color.media){var media=matchMedia("(prefers-color-scheme: light)"),input=document.querySelector(media.matches?"[data-md-color-media='(prefers-color-scheme: light)']":"[data-md-color-media='(prefers-color-scheme: dark)']");palette.color.media=input.getAttribute("data-md-color-media"),palette.color.scheme=input.getAttribute("data-md-color-scheme"),palette.color.primary=input.getAttribute("data-md-color-primary"),palette.color.accent=input.getAttribute("data-md-color-accent")}for(var[key,value]of Object.entries(palette.color))document.body.setAttribute("data-md-color-"+key,value)}</script>
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-header__button md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
</label>
|
||
<div class="md-search" data-md-component="search" role="dialog">
|
||
<label class="md-search__overlay" for="__search"></label>
|
||
<div class="md-search__inner" role="search">
|
||
<form class="md-search__form" name="search">
|
||
<input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required>
|
||
<label class="md-search__icon md-icon" for="__search">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.52 6.52 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5"/></svg>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11z"/></svg>
|
||
</label>
|
||
<nav class="md-search__options" aria-label="Search">
|
||
|
||
<a href="javascript:void(0)" class="md-search__icon md-icon" title="Share" aria-label="Share" data-clipboard data-clipboard-text="" data-md-component="search-share" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9a3 3 0 0 0-3 3 3 3 0 0 0 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.15c-.05.21-.08.43-.08.66 0 1.61 1.31 2.91 2.92 2.91s2.92-1.3 2.92-2.91A2.92 2.92 0 0 0 18 16.08"/></svg>
|
||
</a>
|
||
|
||
<button type="reset" class="md-search__icon md-icon" title="Clear" aria-label="Clear" tabindex="-1">
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>
|
||
</button>
|
||
</nav>
|
||
|
||
<div class="md-search__suggest" data-md-component="search-suggest"></div>
|
||
|
||
</form>
|
||
<div class="md-search__output">
|
||
<div class="md-search__scrollwrap" tabindex="0" data-md-scrollfix>
|
||
<div class="md-search-result" data-md-component="search-result">
|
||
<div class="md-search-result__meta">
|
||
Initializing search
|
||
</div>
|
||
<ol class="md-search-result__list" role="presentation"></ol>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
</nav>
|
||
|
||
</header>
|
||
|
||
<div class="md-container" data-md-component="container">
|
||
|
||
|
||
|
||
|
||
|
||
<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs">
|
||
<div class="md-grid">
|
||
<ul class="md-tabs__list">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href=".." class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Home
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../01_overview/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Overview
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item md-tabs__item--active">
|
||
<a href="./" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
App Components
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../03_start_an_app/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Start an App
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../05_mcp_setup/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
MCP
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../04_development/01_testing/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Testing & Quality
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-tabs__item">
|
||
<a href="../04_development/02_releasing/" class="md-tabs__link">
|
||
|
||
|
||
|
||
|
||
|
||
Releasing & Versioning
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</div>
|
||
</nav>
|
||
|
||
|
||
|
||
<main class="md-main" data-md-component="main">
|
||
<div class="md-main__inner md-grid">
|
||
|
||
|
||
|
||
<div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" >
|
||
<div class="md-sidebar__scrollwrap">
|
||
<div class="md-sidebar__inner">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--primary md-nav--lifted md-nav--integrated" aria-label="Navigation" data-md-level="0">
|
||
<label class="md-nav__title" for="__drawer">
|
||
<a href=".." title="Aetos Standards — Wiki" class="md-nav__button md-logo" aria-label="Aetos Standards — Wiki" data-md-component="logo">
|
||
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M12 8a3 3 0 0 0 3-3 3 3 0 0 0-3-3 3 3 0 0 0-3 3 3 3 0 0 0 3 3m0 3.54C9.64 9.35 6.5 8 3 8v11c3.5 0 6.64 1.35 9 3.54 2.36-2.19 5.5-3.54 9-3.54V8c-3.5 0-6.64 1.35-9 3.54"/></svg>
|
||
|
||
</a>
|
||
Aetos Standards — Wiki
|
||
</label>
|
||
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href=".." class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Home
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../01_overview/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Overview
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--active">
|
||
|
||
<input class="md-nav__toggle md-toggle" type="checkbox" id="__toc">
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__link md-nav__link--active" for="__toc">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
App Components
|
||
|
||
</span>
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<a href="./" class="md-nav__link md-nav__link--active">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
App Components
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
|
||
|
||
|
||
<nav class="md-nav md-nav--secondary" aria-label="Table of contents">
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<label class="md-nav__title" for="__toc">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
Table of contents
|
||
</label>
|
||
<ul class="md-nav__list" data-md-component="toc" data-md-scrollfix>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#component-model" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
🗂️ Component model
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="🗂️ Component model">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#runtime-flow" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
🔀 Runtime flow
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#composition-root-wiring" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
🧵 Composition-root wiring
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#component-roles" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
📦 Component roles
|
||
</span>
|
||
</a>
|
||
|
||
<nav class="md-nav" aria-label="📦 Component roles">
|
||
<ul class="md-nav__list">
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#1-interfaces-the-edge" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
1. interfaces/ — the edge
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#2-domain-the-core" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
2. domain/ — the core
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#3-infra-the-adapters" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
3. infra/ — the adapters
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#4-pipelines-data-driven-families-optional" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
4. pipelines/ — data-driven families (optional)
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#5-workerpy-the-job-worker-optional" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
5. worker.py — the job worker (optional)
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#6-initpy-the-composition-root" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
6. init.py — the composition root
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#how-the-request-flows" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
🔗 How the request flows
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
<li class="md-nav__item">
|
||
<a href="#related" class="md-nav__link">
|
||
<span class="md-ellipsis">
|
||
Related
|
||
</span>
|
||
</a>
|
||
|
||
</li>
|
||
|
||
</ul>
|
||
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../03_start_an_app/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Start an App
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item md-nav__item--nested">
|
||
|
||
|
||
|
||
|
||
|
||
<input class="md-nav__toggle md-toggle md-toggle--indeterminate" type="checkbox" id="__nav_5" >
|
||
|
||
|
||
<label class="md-nav__link" for="__nav_5" id="__nav_5_label" tabindex="0">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
MCP
|
||
|
||
</span>
|
||
|
||
|
||
<span class="md-nav__icon md-icon"></span>
|
||
</label>
|
||
|
||
<nav class="md-nav" data-md-level="1" aria-labelledby="__nav_5_label" aria-expanded="false">
|
||
<label class="md-nav__title" for="__nav_5">
|
||
<span class="md-nav__icon md-icon"></span>
|
||
MCP
|
||
</label>
|
||
<ul class="md-nav__list" data-md-scrollfix>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../05_mcp_setup/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Setup
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../04_development/01_testing/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Testing & Quality
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<li class="md-nav__item">
|
||
<a href="../04_development/02_releasing/" class="md-nav__link">
|
||
|
||
|
||
|
||
<span class="md-ellipsis">
|
||
Releasing & Versioning
|
||
|
||
</span>
|
||
|
||
|
||
</a>
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</nav>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<div class="md-content" data-md-component="content">
|
||
<article class="md-content__inner md-typeset">
|
||
|
||
|
||
|
||
|
||
|
||
<h1 id="app-components">App Components</h1>
|
||
<p>What ships in every standard app, and how the pieces relate. This is the
|
||
blueprint's component model — <code>templates/app/</code> is a runnable instance of it.</p>
|
||
<hr />
|
||
<h2 id="component-model">🗂️ Component model</h2>
|
||
<p>The blueprint has two orthogonal views: <strong>runtime flow</strong> (who calls whom) and
|
||
<strong>composition-root wiring</strong> (who constructs whom). Keeping them separate makes
|
||
the layering visible instead of hiding it behind a hub.</p>
|
||
<h3 id="runtime-flow">🔀 Runtime flow</h3>
|
||
<p>Who calls whom at request and job time:</p>
|
||
<pre class="mermaid"><code>%%{init: {"flowchart": {"nodeSpacing": 50, "rankSpacing": 80}}}%%
|
||
flowchart TB
|
||
subgraph REQ["Request path — API process"]
|
||
direction LR
|
||
CL(["Client<br/>operationId"]) --> RT["interfaces/&lt;resource&gt;/routes.py"]
|
||
RT --> SVC["&lt;resource&gt;_svc"]
|
||
SVC --> REPO["&lt;resource&gt;_repo"]
|
||
end
|
||
|
||
subgraph WRK["Background path — worker process"]
|
||
direction LR
|
||
W["worker.py"] --> JSVC["job_svc"]
|
||
W --> PIPE["pipelines/&lt;family&gt;"]
|
||
PIPE --> ISVC["item_svc"]
|
||
JSVC --> JREPO["job_repo"]
|
||
ISVC --> IREPO["item_repo"]
|
||
end
|
||
|
||
subgraph INFRA["infra/ — adapters"]
|
||
MONGO[("MongoDB / mongo_ops")]
|
||
end
|
||
|
||
REPO --> MONGO
|
||
JREPO --> MONGO
|
||
IREPO --> MONGO</code></pre>
|
||
<blockquote>
|
||
<p><strong>Legend</strong> — solid arrows are runtime calls. Nothing is self-constructed.</p>
|
||
</blockquote>
|
||
<h3 id="composition-root-wiring">🧵 Composition-root wiring</h3>
|
||
<p>Who constructs what at startup:</p>
|
||
<pre class="mermaid"><code>%%{init: {"flowchart": {"nodeSpacing": 50, "rankSpacing": 80}}}%%
|
||
flowchart TB
|
||
INIT["init.py — composition root"]
|
||
SETUP["infra/db/setup.py"]
|
||
FACT["domain/*/ports/*<br/>factory seams"]
|
||
REPOS["repos<br/>infra/db/repos/mongo.py"]
|
||
SVCES["services<br/>CrudsServiceAdaptor"]
|
||
STATE["app.state.db_resources"]
|
||
HEXA["hexa.build"]
|
||
RT["routes.py"]
|
||
WK["worker.py"]
|
||
|
||
INIT --> SETUP
|
||
INIT --> FACT
|
||
FACT --> REPOS
|
||
FACT --> SVCES
|
||
REPOS --> SVCES
|
||
HEXA -.-> SVCES
|
||
INIT --> STATE
|
||
STATE --> RT
|
||
INIT --> WK</code></pre>
|
||
<blockquote>
|
||
<p><strong>Legend</strong> — <code>init.py</code> is the only place that imports concrete adapters. The
|
||
<code>ports/</code> factory seams bind <code>infra</code>; the API reads resources from
|
||
<code>app.state.db_resources</code>, the worker calls <code>init()</code> directly.</p>
|
||
</blockquote>
|
||
<hr />
|
||
<h2 id="component-roles">📦 Component roles</h2>
|
||
<h3 id="1-interfaces-the-edge">1. <code>interfaces/</code> — the edge</h3>
|
||
<p>Thin handlers bound to <code>openapi.yaml</code> <code>operationId</code>s. They parse the request,
|
||
call a service, and shape the response. No business rules, no I/O.</p>
|
||
<ul>
|
||
<li><code>interfaces/openapi.py</code> re-exports every route module so the OpenAPI-first
|
||
app can resolve handlers.</li>
|
||
<li>Routes may mount the <code>cache</code> decorator on read endpoints.</li>
|
||
</ul>
|
||
<h3 id="2-domain-the-core">2. <code>domain/</code> — the core</h3>
|
||
<p>Models, ports, and services (use cases). No framework imports. The <code>ports/</code>
|
||
package is the one seam allowed to reach into <code>infra/</code>: each port module declares
|
||
the document shape and a factory that binds the adapter.</p>
|
||
<ul>
|
||
<li><code><aggregate>/models.py</code> — dataclasses, enums, document shapes.</li>
|
||
<li><code><aggregate>/ports/db.py</code> — document model + <code>create_<aggregate>_repo()</code>.</li>
|
||
<li><code><aggregate>/ports/service.py</code> — service + <code>get_<aggregate>_service(repo)</code>.</li>
|
||
<li>Services compose ports and enforce the business rules; the port factories bind
|
||
the implementations, and the composition root calls them.</li>
|
||
</ul>
|
||
<h3 id="3-infra-the-adapters">3. <code>infra/</code> — the adapters</h3>
|
||
<p>Concrete implementations of the domain ports plus cross-cutting infrastructure:</p>
|
||
<ul>
|
||
<li><code>db/</code> — mongo-ops repositories and the connection lifecycle.</li>
|
||
<li><code>adapters/</code> — cache store, progress bus, storages, fetchers (library-backed).</li>
|
||
<li>Anything else the domain needs, implemented against its ports.</li>
|
||
</ul>
|
||
<h3 id="4-pipelines-data-driven-families-optional">4. <code>pipelines/</code> — data-driven families <em>(optional)</em></h3>
|
||
<p>Extraction and enrichment families, each a deterministic step chain
|
||
(<code>abc.py</code> / <code>impl.py</code> / <code>pipeline.yaml</code> / <code>resources/</code>). Roots are registered
|
||
in the pattern registry and driven either by a worker job or a CLI.</p>
|
||
<h3 id="5-workerpy-the-job-worker-optional">5. <code>worker.py</code> — the job worker <em>(optional)</em></h3>
|
||
<p>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.</p>
|
||
<h3 id="6-initpy-the-composition-root">6. <code>init.py</code> — the composition root</h3>
|
||
<p>The only place anything is constructed: leaf repositories → dependent
|
||
repositories → services → adapters. Exposes <code>get_svc</code> overloads and starts /
|
||
stops resources on the app lifespan.</p>
|
||
<hr />
|
||
<h2 id="how-the-request-flows">🔗 How the request flows</h2>
|
||
<pre class="mermaid"><code>sequenceDiagram
|
||
participant C as Client
|
||
participant R as route (interfaces)
|
||
participant S as service (port factory / adapter)
|
||
participant P as repo (infra)
|
||
participant DB as MongoDB
|
||
|
||
C->>R: POST /items (operationId)
|
||
R->>S: svc.create(payload)
|
||
S->>P: repo.insert(doc)
|
||
P->>DB: insert
|
||
DB-->>P: ack
|
||
P-->>S: doc
|
||
S-->>R: result
|
||
R-->>C: 200 JSON</code></pre>
|
||
<hr />
|
||
<h2 id="related">Related</h2>
|
||
<ul>
|
||
<li><a href="../01_overview/">01 – Overview</a> · <a href="../03_start_an_app/">03 – Start an App</a> · <a href="../04_development/01_testing/">04 – Testing & Quality</a></li>
|
||
</ul>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</article>
|
||
</div>
|
||
|
||
|
||
<script>var tabs=__md_get("__tabs");if(Array.isArray(tabs))e:for(var set of document.querySelectorAll(".tabbed-set")){var labels=set.querySelector(".tabbed-labels");for(var tab of tabs)for(var label of labels.getElementsByTagName("label"))if(label.innerText.trim()===tab){var input=document.getElementById(label.htmlFor);input.checked=!0;continue e}}</script>
|
||
|
||
<script>var target=document.getElementById(location.hash.slice(1));target&&target.name&&(target.checked=target.name.startsWith("__tabbed_"))</script>
|
||
</div>
|
||
|
||
<button type="button" class="md-top md-icon" data-md-component="top" hidden>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M13 20h-2V8l-5.5 5.5-1.42-1.42L12 4.16l7.92 7.92-1.42 1.42L13 8z"/></svg>
|
||
Back to top
|
||
</button>
|
||
|
||
</main>
|
||
|
||
<footer class="md-footer">
|
||
|
||
<div class="md-footer-meta md-typeset">
|
||
<div class="md-footer-meta__inner md-grid">
|
||
<div class="md-copyright">
|
||
|
||
|
||
Made with
|
||
<a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener">
|
||
Material for MkDocs
|
||
</a>
|
||
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
</footer>
|
||
|
||
</div>
|
||
<div class="md-dialog" data-md-component="dialog">
|
||
<div class="md-dialog__inner md-typeset"></div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
<script id="__config" type="application/json">{"base": "..", "features": ["navigation.sections", "navigation.expand", "navigation.top", "navigation.instant", "navigation.tracking", "navigation.indexes", "navigation.tabs", "toc.integrate", "header.autohide", "content.code.copy", "content.code.annotate", "content.tabs.link", "content.action.edit", "search.highlight", "search.share", "search.suggest"], "search": "../assets/javascripts/workers/search.973d3a69.min.js", "tags": null, "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version": "Select version"}, "version": null}</script>
|
||
|
||
|
||
<script src="../assets/javascripts/bundle.f55a23d4.min.js"></script>
|
||
|
||
<script src="https://unpkg.com/mermaid@10.9.1/dist/mermaid.min.js"></script>
|
||
|
||
|
||
</body>
|
||
</html> |