docs(standards): align layering docs with the port seam; split runtime vs wiring diagrams
This commit is contained in:
@@ -433,6 +433,30 @@
|
||||
</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">
|
||||
@@ -595,55 +619,66 @@
|
||||
blueprint's component model — <code>templates/app/</code> is a runnable instance of it.</p>
|
||||
<hr />
|
||||
<h2 id="component-model">🗂️ Component model</h2>
|
||||
<pre class="mermaid"><code>%%{init: {"flowchart": {"nodeSpacing": 55, "rankSpacing": 120}}}%%
|
||||
<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 EDGE["interfaces/ — HTTP edge"]
|
||||
OAS["openapi.yaml"]
|
||||
RU["resource routes"]
|
||||
JU["job routes"]
|
||||
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 CORE["domain/ — business core"]
|
||||
RI["<resource> · models · ports · services"]
|
||||
JI["<job/worker> · models · ports · services"]
|
||||
PI["pipelines/<family>"]
|
||||
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"]
|
||||
DB[("MongoDB repos")]
|
||||
CA["redis cache"]
|
||||
ST["stores · progress bus"]
|
||||
LL["llm clients"]
|
||||
MONGO[("MongoDB / mongo_ops")]
|
||||
end
|
||||
|
||||
W["worker.py"]
|
||||
CR["init.py — composition root"]
|
||||
|
||||
OAS --> RU
|
||||
OAS --> JU
|
||||
RU --> RI
|
||||
JU --> JI
|
||||
JI --> PI
|
||||
PI --> DB
|
||||
PI --> LL
|
||||
RI --> CA
|
||||
RI --> DB
|
||||
JI --> ST
|
||||
W --> JI
|
||||
W --> PI
|
||||
|
||||
CR === RU
|
||||
CR === JU
|
||||
CR === RI
|
||||
CR === JI
|
||||
CR === PI
|
||||
CR === W
|
||||
CR === DB
|
||||
CR === CA
|
||||
CR === ST</code></pre>
|
||||
REPO --> MONGO
|
||||
JREPO --> MONGO
|
||||
IREPO --> MONGO</code></pre>
|
||||
<blockquote>
|
||||
<p><strong>Legend</strong> — solid arrows are direct calls; thick links are wires created by
|
||||
the composition root. Every component is injected, never self-constructed.</p>
|
||||
<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>
|
||||
@@ -656,14 +691,15 @@ call a service, and shape the response. No business rules, no I/O.</p>
|
||||
<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>Pure models, <strong>ports</strong> (abstract contracts), and services (use cases). No
|
||||
framework imports, no drivers.</p>
|
||||
<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> — repo contract; <code><aggregate>/ports/service.py</code> —
|
||||
service contract.</li>
|
||||
<li>Services compose ports and enforce the business rules; the composition root
|
||||
injects the implementations.</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>
|
||||
@@ -689,7 +725,7 @@ stops resources on the app lifespan.</p>
|
||||
<pre class="mermaid"><code>sequenceDiagram
|
||||
participant C as Client
|
||||
participant R as route (interfaces)
|
||||
participant S as service (domain)
|
||||
participant S as service (port factory / adapter)
|
||||
participant P as repo (infra)
|
||||
participant DB as MongoDB
|
||||
|
||||
|
||||
Reference in New Issue
Block a user