feat(standards): codify codegen, ship generic Tier-B infra, add get_app_template + prompts; fix docs:// regressions
P0: fix doc:// -> docs:// across standards src/server/registry and regenerate stale mongo-ops bundle. P1: build recipe + spec-first routing now document the openapi-first codegen fast path (models/routes with --inject-request/--package). P4: CRUD template documents the model-vs-document id rule (BaseDocument owns id). P5: authless guidance (omit security keys). P6: template gains generic infra (http_cache, keyed SSE bus, KV port+in-memory+Redis), interfaces/openapi.py + .dockerignore + generic build test registered as components, and a get_app_template tool; genericity matrix marks LLM pool + human-in-loop as reference-app patterns. P7: intake/build/start_app/trial prompts. P8: template ships tests/integration/test_openapi_app_builds.py. Rebuilt standards + openapi-first bundles and re-collected.
This commit is contained in:
@@ -400,6 +400,15 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#the-id-rule-model-vs-document" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
The id rule (model vs. document)
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -629,6 +638,15 @@
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
<a href="#the-id-rule-model-vs-document" class="md-nav__link">
|
||||
<span class="md-ellipsis">
|
||||
The id rule (model vs. document)
|
||||
</span>
|
||||
</a>
|
||||
|
||||
</li>
|
||||
|
||||
<li class="md-nav__item">
|
||||
@@ -710,8 +728,53 @@ makes the codebase predictable and generatable.</p>
|
||||
</span><span id="__span-0-5"><a id="__codelineno-0-5" name="__codelineno-0-5"></a> service.py # service class + factory
|
||||
</span><span id="__span-0-6"><a id="__codelineno-0-6" name="__codelineno-0-6"></a> converters.py # (optional) transforms
|
||||
</span></code></pre></div></td></tr></table></div>
|
||||
<h2 id="the-id-rule-model-vs-document">The id rule (model vs. document)</h2>
|
||||
<p>Two different objects, two different jobs:</p>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Object</th>
|
||||
<th>Lives in</th>
|
||||
<th>Has <code>id</code>?</th>
|
||||
<th>Purpose</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><strong>Domain model</strong></td>
|
||||
<td><code>domain/<resource>/models.py</code></td>
|
||||
<td><strong>No</strong></td>
|
||||
<td>business shape, framework-free</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><strong>Document</strong></td>
|
||||
<td><code>domain/<resource>/ports/db.py</code></td>
|
||||
<td><strong>Yes</strong> (from <code>BaseDocument</code>)</td>
|
||||
<td>Mongo persistence shape</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><code>BaseDocument</code> (mongo-ops) supplies <code>id: Optional[PyObjectId]</code> (aliased to
|
||||
<code>_id</code>) plus <code>created_at</code> / <code>updated_at</code>. The domain model must <strong>not</strong> declare
|
||||
<code>id</code>; Mongo owns it.</p>
|
||||
<blockquote>
|
||||
<p><strong>Trap — generated wire models.</strong> <code>openapi-first models</code> produces the HTTP
|
||||
<em>contract</em> (see the <a href="../01_build_recipe/">Build Recipe</a>, step 3). Response
|
||||
schemas frequently declare a required <code>id: str</code>, so a generated class is
|
||||
<strong>not</strong> a domain model. If you write
|
||||
<code>class ItemDocument(Item, BaseDocument)</code> and <code>Item</code> re-exports a required
|
||||
<code>id: str</code>, that field shadows <code>BaseDocument.id</code> and no Mongo <code>_id</code> can ever be
|
||||
set. Fix it one of three ways:</p>
|
||||
<ol>
|
||||
<li><strong>Preferred:</strong> keep the domain model id-free (hand-author it from the CRUD
|
||||
template), and keep the required <code>id</code> only on the wire model.</li>
|
||||
<li>Order the bases so the document wins:
|
||||
<code>class ItemDocument(BaseDocument, Item)</code>.</li>
|
||||
<li>Keep a separate document type and translate with <code>ports/converters.py</code>.</li>
|
||||
</ol>
|
||||
</blockquote>
|
||||
<h2 id="modelspy">models.py</h2>
|
||||
<p>A framework-free Pydantic model describing the business shape.</p>
|
||||
<p>A framework-free Pydantic model describing the business shape (no <code>id</code>).</p>
|
||||
<div class="language-python highlight"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre><span></span><span class="normal"><a href="#__codelineno-1-1">1</a></span>
|
||||
<span class="normal"><a href="#__codelineno-1-2">2</a></span>
|
||||
<span class="normal"><a href="#__codelineno-1-3">3</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-1-1"><a id="__codelineno-1-1" name="__codelineno-1-1"></a><span class="k">class</span><span class="w"> </span><span class="nc">Item</span><span class="p">(</span><span class="n">BaseModel</span><span class="p">):</span>
|
||||
@@ -730,16 +793,18 @@ The repository factory selects the right repository flavour.</p>
|
||||
<span class="normal"><a href="#__codelineno-2-7"> 7</a></span>
|
||||
<span class="normal"><a href="#__codelineno-2-8"> 8</a></span>
|
||||
<span class="normal"><a href="#__codelineno-2-9"> 9</a></span>
|
||||
<span class="normal"><a href="#__codelineno-2-10">10</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1"></a><span class="k">class</span><span class="w"> </span><span class="nc">ItemDocument</span><span class="p">(</span><span class="n">Item</span><span class="p">,</span> <span class="n">BaseDocument</span><span class="p">):</span> <span class="c1"># BaseDocument adds id/created_at/...</span>
|
||||
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2"></a> <span class="k">pass</span>
|
||||
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3"></a>
|
||||
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4"></a><span class="k">def</span><span class="w"> </span><span class="nf">create_item_repo</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">owner_repo</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="n">ItemRepository</span><span class="p">:</span>
|
||||
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5"></a> <span class="k">return</span> <span class="n">repo_factory</span><span class="p">(</span>
|
||||
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6"></a> <span class="n">collection</span><span class="o">=</span><span class="s2">"items"</span><span class="p">,</span>
|
||||
</span><span id="__span-2-7"><a id="__codelineno-2-7" name="__codelineno-2-7"></a> <span class="n">document</span><span class="o">=</span><span class="n">ItemDocument</span><span class="p">,</span>
|
||||
</span><span id="__span-2-8"><a id="__codelineno-2-8" name="__codelineno-2-8"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">PopulateRule</span><span class="p">(</span><span class="n">field</span><span class="o">=</span><span class="s2">"owner"</span><span class="p">,</span> <span class="n">collection</span><span class="o">=</span><span class="s2">"users"</span><span class="p">)],</span>
|
||||
</span><span id="__span-2-9"><a id="__codelineno-2-9" name="__codelineno-2-9"></a> <span class="n">cache</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="c1"># or a CachedBaseRepository config</span>
|
||||
</span><span id="__span-2-10"><a id="__codelineno-2-10" name="__codelineno-2-10"></a> <span class="p">)</span>
|
||||
<span class="normal"><a href="#__codelineno-2-10">10</a></span>
|
||||
<span class="normal"><a href="#__codelineno-2-11">11</a></span></pre></div></td><td class="code"><div><pre><span></span><code><span id="__span-2-1"><a id="__codelineno-2-1" name="__codelineno-2-1"></a><span class="c1"># Item is id-free; BaseDocument adds id/created_at/updated_at.</span>
|
||||
</span><span id="__span-2-2"><a id="__codelineno-2-2" name="__codelineno-2-2"></a><span class="k">class</span><span class="w"> </span><span class="nc">ItemDocument</span><span class="p">(</span><span class="n">Item</span><span class="p">,</span> <span class="n">BaseDocument</span><span class="p">):</span>
|
||||
</span><span id="__span-2-3"><a id="__codelineno-2-3" name="__codelineno-2-3"></a> <span class="k">pass</span>
|
||||
</span><span id="__span-2-4"><a id="__codelineno-2-4" name="__codelineno-2-4"></a>
|
||||
</span><span id="__span-2-5"><a id="__codelineno-2-5" name="__codelineno-2-5"></a><span class="k">def</span><span class="w"> </span><span class="nf">create_item_repo</span><span class="p">(</span><span class="o">*</span><span class="p">,</span> <span class="n">owner_repo</span><span class="o">=</span><span class="kc">None</span><span class="p">)</span> <span class="o">-></span> <span class="n">ItemRepository</span><span class="p">:</span>
|
||||
</span><span id="__span-2-6"><a id="__codelineno-2-6" name="__codelineno-2-6"></a> <span class="k">return</span> <span class="n">repo_factory</span><span class="p">(</span>
|
||||
</span><span id="__span-2-7"><a id="__codelineno-2-7" name="__codelineno-2-7"></a> <span class="n">collection</span><span class="o">=</span><span class="s2">"items"</span><span class="p">,</span>
|
||||
</span><span id="__span-2-8"><a id="__codelineno-2-8" name="__codelineno-2-8"></a> <span class="n">document</span><span class="o">=</span><span class="n">ItemDocument</span><span class="p">,</span>
|
||||
</span><span id="__span-2-9"><a id="__codelineno-2-9" name="__codelineno-2-9"></a> <span class="n">populate_rules</span><span class="o">=</span><span class="p">[</span><span class="n">PopulateRule</span><span class="p">(</span><span class="n">field</span><span class="o">=</span><span class="s2">"owner"</span><span class="p">,</span> <span class="n">collection</span><span class="o">=</span><span class="s2">"users"</span><span class="p">)],</span>
|
||||
</span><span id="__span-2-10"><a id="__codelineno-2-10" name="__codelineno-2-10"></a> <span class="n">cache</span><span class="o">=</span><span class="kc">None</span><span class="p">,</span> <span class="c1"># or a CachedBaseRepository config</span>
|
||||
</span><span id="__span-2-11"><a id="__codelineno-2-11" name="__codelineno-2-11"></a> <span class="p">)</span>
|
||||
</span></code></pre></div></td></tr></table></div>
|
||||
<h2 id="portsservicepy">ports/service.py</h2>
|
||||
<p>The service is built through the DI library, injecting the repository. The
|
||||
|
||||
Reference in New Issue
Block a user