docs(html): di + data-flow explainers reflect BindContext
This commit is contained in:
@@ -1664,7 +1664,7 @@ footer .colophon {
|
|||||||
</div>
|
</div>
|
||||||
<div class="di-card">
|
<div class="di-card">
|
||||||
<h4>Two binding modes, one symbol.</h4>
|
<h4>Two binding modes, one symbol.</h4>
|
||||||
<p>The <code>BlogModule</code> binds <code>IArticlesRepository</code> to <code>MockArticlesRepository</code> by default — useful at dev/test time. At app boot, <code>bindProductionBlog(config, tracer, logger, bus, queue)</code> unbinds the symbol and rebinds it to <code>new ArticlesRepository(config, tracer, logger)</code>. Use cases and controllers don't notice — they get whatever the symbol currently resolves to.</p>
|
<p>The <code>BlogModule</code> binds <code>IArticlesRepository</code> to <code>MockArticlesRepository</code> by default — useful at dev/test time. At app boot, <code>bindProductionBlog(ctx: BindProductionContext)</code> unbinds the symbol and rebinds it to <code>new ArticlesRepository(ctx.config, ctx.tracer, ctx.logger)</code>. Use cases and controllers don't notice — they get whatever the symbol currently resolves to. The <code>ctx</code> object is built once by the app aggregator and passed to all feature binders.</p>
|
||||||
<p>This is also why the boundary stays clean: features don't import <code>core-cms</code>; the app passes the Payload config in.</p>
|
<p>This is also why the boundary stays clean: features don't import <code>core-cms</code>; the app passes the Payload config in.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1701,8 +1701,9 @@ footer .colophon {
|
|||||||
<div class="cf-card">
|
<div class="cf-card">
|
||||||
<div class="cf-tag">app boot · production override</div>
|
<div class="cf-tag">app boot · production override</div>
|
||||||
<h3>blog/di/<em>bind-production.ts</em></h3>
|
<h3>blog/di/<em>bind-production.ts</em></h3>
|
||||||
<p>Called from each app's bootstrap (<code>apps/web-next/src/server/bind-production.ts</code>) with the resolved Payload config.</p>
|
<p>Called from each app's bootstrap (<code>apps/web-next/src/server/bind-production.ts</code>) with the <code>ctx</code> object built once by the aggregator. <code>BindProductionContext</code> is imported from <code>@repo/core-shared/di</code>.</p>
|
||||||
<pre class="code" data-lang="typescript // packages/blog/src/di/bind-production.ts"><span class="k">export function</span> <span class="n">bindProductionBlog</span>(<span class="n">config</span>: <span class="t">SanitizedConfig</span>): <span class="t">void</span> {
|
<pre class="code" data-lang="typescript // packages/blog/src/di/bind-production.ts"><span class="k">export function</span> <span class="n">bindProductionBlog</span>(<span class="n">ctx</span>: <span class="t">BindProductionContext</span>): <span class="t">void</span> {
|
||||||
|
<span class="k">const</span> { <span class="n">config</span>, <span class="n">tracer</span>, <span class="n">logger</span>, <span class="n">bus</span>, <span class="n">queue</span>, <span class="n">realtime</span>, <span class="n">realtimeRegistry</span> } = <span class="n">ctx</span>;
|
||||||
<span class="k">if</span> (<span class="n">blogContainer</span>.<span class="n">isBound</span>(<span class="n">BLOG_SYMBOLS</span>.<span class="n">IArticlesRepository</span>)) {
|
<span class="k">if</span> (<span class="n">blogContainer</span>.<span class="n">isBound</span>(<span class="n">BLOG_SYMBOLS</span>.<span class="n">IArticlesRepository</span>)) {
|
||||||
<span class="n">blogContainer</span>.<span class="n">unbind</span>(<span class="n">BLOG_SYMBOLS</span>.<span class="n">IArticlesRepository</span>);
|
<span class="n">blogContainer</span>.<span class="n">unbind</span>(<span class="n">BLOG_SYMBOLS</span>.<span class="n">IArticlesRepository</span>);
|
||||||
}
|
}
|
||||||
@@ -1773,7 +1774,7 @@ footer .colophon {
|
|||||||
|
|
||||||
<p style="margin-top: 32px;"><strong>The mock is reached from two directions.</strong> Both are legitimate, neither is "the test version":</p>
|
<p style="margin-top: 32px;"><strong>The mock is reached from two directions.</strong> Both are legitimate, neither is "the test version":</p>
|
||||||
<ol class="role-jobs">
|
<ol class="role-jobs">
|
||||||
<li><strong>By the DI container at runtime.</strong> <code>BlogModule</code> binds <code>IArticlesRepository</code> to <code>MockArticlesRepository</code> at module-load time. Anything resolving that symbol — use cases, controllers, tRPC procedures, the dev server — gets the mock until <code>bindProductionBlog(config, tracer, logger, bus, queue)</code> swaps it for the real Payload-backed one. See §03.</li>
|
<li><strong>By the DI container at runtime.</strong> <code>BlogModule</code> binds <code>IArticlesRepository</code> to <code>MockArticlesRepository</code> at module-load time. Anything resolving that symbol — use cases, controllers, tRPC procedures, the dev server — gets the mock until <code>bindProductionBlog(ctx: BindProductionContext)</code> swaps it for the real Payload-backed one. See §03.</li>
|
||||||
<li><strong>By tests, via direct construction.</strong> Unit tests skip the container entirely. They construct the mock with <code>new MockArticlesRepository()</code> and pass it directly into the use-case factory function. Same class, different consumer — just a closure with a fake repo.</li>
|
<li><strong>By tests, via direct construction.</strong> Unit tests skip the container entirely. They construct the mock with <code>new MockArticlesRepository()</code> and pass it directly into the use-case factory function. Same class, different consumer — just a closure with a fake repo.</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
@@ -1786,7 +1787,7 @@ footer .colophon {
|
|||||||
<details class="cf-detail">
|
<details class="cf-detail">
|
||||||
<summary>show: the mock as DI binding (in module.ts)</summary>
|
<summary>show: the mock as DI binding (in module.ts)</summary>
|
||||||
<div class="detail-body">
|
<div class="detail-body">
|
||||||
<p>This is from <code>packages/blog/src/di/module.ts</code> — the very first binding in the module is the mock. Everything downstream (use cases, controllers) resolves through this default. <code>bindProductionBlog(config, tracer, logger, bus, queue)</code> later replaces only this one line at app boot — use case + controller bindings stay put.</p>
|
<p>This is from <code>packages/blog/src/di/module.ts</code> — the very first binding in the module is the mock. Everything downstream (use cases, controllers) resolves through this default. <code>bindProductionBlog(ctx: BindProductionContext)</code> later replaces only this one line at app boot — use case + controller bindings stay put.</p>
|
||||||
<pre class="code" data-lang="typescript // packages/blog/src/di/module.ts"><span class="k">export const</span> <span class="n">BlogModule</span> = <span class="k">new</span> <span class="t">ContainerModule</span>((<span class="n">bind</span>) => {
|
<pre class="code" data-lang="typescript // packages/blog/src/di/module.ts"><span class="k">export const</span> <span class="n">BlogModule</span> = <span class="k">new</span> <span class="t">ContainerModule</span>((<span class="n">bind</span>) => {
|
||||||
<span class="c">// 1) Mock is the DEFAULT binding for the repo symbol.</span>
|
<span class="c">// 1) Mock is the DEFAULT binding for the repo symbol.</span>
|
||||||
<span class="c">// Dev server, unit tests, storybook all resolve to this.</span>
|
<span class="c">// Dev server, unit tests, storybook all resolve to this.</span>
|
||||||
@@ -2191,7 +2192,7 @@ footer .colophon {
|
|||||||
<div class="tradeoff-card">
|
<div class="tradeoff-card">
|
||||||
<div class="tag">di/bind-production.ts</div>
|
<div class="tag">di/bind-production.ts</div>
|
||||||
<h3>Production binder</h3>
|
<h3>Production binder</h3>
|
||||||
<p class="blurb"><code>bindProduction<F>(config)</code> — unbinds the mock, rebinds the real Payload-backed impl.</p>
|
<p class="blurb"><code>bindProduction<F>(ctx: BindProductionContext)</code> — unbinds the mock, rebinds the real Payload-backed impl. The <code>ctx</code> arg carries required fields (<code>tracer</code>, <code>logger</code>, <code>config</code>) and optional cross-cutting deps (<code>bus</code>, <code>queue</code>, <code>realtime</code>, <code>realtimeRegistry</code>).</p>
|
||||||
<div class="pc-cols">
|
<div class="pc-cols">
|
||||||
<div class="pros"><h5>Pros</h5><ul>
|
<div class="pros"><h5>Pros</h5><ul>
|
||||||
<li>Decouples Payload config from the feature package — boundary stays clean</li>
|
<li>Decouples Payload config from the feature package — boundary stays clean</li>
|
||||||
|
|||||||
@@ -756,7 +756,7 @@ footer .colophon {
|
|||||||
<div class="cast-tag">file 04 · prod swap</div>
|
<div class="cast-tag">file 04 · prod swap</div>
|
||||||
<h3>bind-production.ts</h3>
|
<h3>bind-production.ts</h3>
|
||||||
<p class="role">Replaces the mock repository binding with a real Payload-backed one at app boot.</p>
|
<p class="role">Replaces the mock repository binding with a real Payload-backed one at app boot.</p>
|
||||||
<p>Exports <code>bindProductionBlog(config, tracer, logger, bus, queue)</code>. Function body: <code>blogContainer.unbind(symbol)</code> if already bound, then <code>.bind(symbol).toConstantValue(new ArticlesRepository(config, tracer, logger))</code>. Use cases and controllers are wrapped via <code>withSpan(withCapture(factory(deps)))</code> at bind time so they inherit instrumentation without changing their factory bodies. The <code>bus</code> and <code>queue</code> params come from the app's <code>resolveEventsAndJobs*</code> step (ADR-015) and feed the <code>// <gen:event-handlers></code> / <code>// <gen:jobs></code> injection sites.</p>
|
<p>Exports <code>bindProductionBlog(ctx: BindProductionContext)</code>. Destructures <code>{ config, tracer, logger, bus, queue, realtime, realtimeRegistry }</code> from <code>ctx</code>. Function body: <code>blogContainer.unbind(symbol)</code> if already bound, then <code>.bind(symbol).toConstantValue(new ArticlesRepository(config, tracer, logger))</code>. Use cases and controllers are wrapped via <code>withSpan(withCapture(factory(deps)))</code> at bind time so they inherit instrumentation without changing their factory bodies. The optional <code>bus</code> and <code>queue</code> fields come from the app's <code>resolveEventsAndJobs*</code> step (ADR-015) and feed the <code>// <gen:event-handlers></code> / <code>// <gen:jobs></code> injection sites. <code>BindProductionContext</code> is imported from <code>@repo/core-shared/di</code> — features never import the optional packages directly for the binder signature.</p>
|
||||||
<div class="runs-when"><strong>When it runs:</strong> called from app boot (<code>apps/web-next/src/server/bind-production.ts</code>) when <code>USE_DEV_SEED ≠ "true"</code> AND Payload config is resolvable.</div>
|
<div class="runs-when"><strong>When it runs:</strong> called from app boot (<code>apps/web-next/src/server/bind-production.ts</code>) when <code>USE_DEV_SEED ≠ "true"</code> AND Payload config is resolvable.</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1190,7 +1190,9 @@ footer .colophon {
|
|||||||
└─ Noop or Sentry binders ← bind to sharedContainer
|
└─ Noop or Sentry binders ← bind to sharedContainer
|
||||||
└─ resolveEventsAndJobs*() ← ADR-015 (env-driven bus + queue)
|
└─ resolveEventsAndJobs*() ← ADR-015 (env-driven bus + queue)
|
||||||
└─ Payload-backed in prod, in-memory in dev-seed
|
└─ Payload-backed in prod, in-memory in dev-seed
|
||||||
└─ bindProductionX(config, tracer, logger, bus, queue)
|
└─ build ctx: BindProductionContext = { config, tracer, logger, bus, queue, realtime, realtimeRegistry }
|
||||||
|
└─ required: tracer, logger, config | optional: bus, queue, realtime, realtimeRegistry
|
||||||
|
└─ bindProductionX(ctx) ← single ctx object passed to each feature binder
|
||||||
└─ feature container also binds TRACER + LOGGER
|
└─ feature container also binds TRACER + LOGGER
|
||||||
└─ withSpan(withCapture(...)) at every use case + controller
|
└─ withSpan(withCapture(...)) at every use case + controller
|
||||||
└─ // <gen:event-handlers> / // <gen:jobs> injection sites</code></pre>
|
└─ // <gen:event-handlers> / // <gen:jobs> injection sites</code></pre>
|
||||||
|
|||||||
Reference in New Issue
Block a user