cleanup phases 6
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Runtime services — deployment topology</title>
|
||||
<style>
|
||||
:root{
|
||||
--bg:#faf9f6; --fg:#26221c; --muted:#6d675e; --panel:#ffffff; --border:#ddd7cc;
|
||||
--accent:#0e7a63; --accent-soft:#e4f2ee; --code-bg:#f1ede5; --th-bg:#efeadf;
|
||||
--warn:#a04b12; color-scheme: light dark;
|
||||
}
|
||||
@media (prefers-color-scheme: dark){
|
||||
:root{
|
||||
--bg:#191714; --fg:#e8e3da; --muted:#a29a8d; --panel:#211e1a; --border:#3a352d;
|
||||
--accent:#4fc3a8; --accent-soft:#1e3630; --code-bg:#2a261f; --th-bg:#2d2921;
|
||||
--warn:#e09355;
|
||||
}
|
||||
}
|
||||
*{box-sizing:border-box}
|
||||
body{margin:0;background:var(--bg);color:var(--fg);
|
||||
font:16px/1.62 ui-sans-serif,system-ui,"Segoe UI",Roboto,"Vazirmatn",sans-serif;}
|
||||
main{max-width:72rem;margin:0 auto;padding:2.5rem 1.5rem 5rem;}
|
||||
h1{font-size:1.75rem;line-height:1.25;margin:.2rem 0 1rem;}
|
||||
h2{font-size:1.35rem;margin:2.4rem 0 .7rem;padding-top:1rem;border-top:1px solid var(--border);}
|
||||
h3{font-size:1.08rem;margin:1.8rem 0 .5rem;color:var(--accent);}
|
||||
p{margin:.6rem 0;}
|
||||
a{color:var(--accent);text-decoration:none;} a:hover{text-decoration:underline;}
|
||||
code{background:var(--code-bg);border-radius:4px;padding:.1em .35em;
|
||||
font:.86em ui-monospace,"Cascadia Code",Consolas,monospace;overflow-wrap:anywhere;}
|
||||
pre{background:var(--code-bg);border:1px solid var(--border);border-radius:8px;
|
||||
padding: .9rem 1rem;overflow-x:auto;}
|
||||
pre code{background:none;padding:0;}
|
||||
hr{border:none;border-top:1px solid var(--border);margin:2rem 0;}
|
||||
.tblwrap{overflow-x:auto;margin:1rem 0;border:1px solid var(--border);border-radius:8px;}
|
||||
table{border-collapse:collapse;width:100%;font-size:.92rem;}
|
||||
th{background:var(--th-bg);text-align:start;position:sticky;top:0;}
|
||||
th,td{border-bottom:1px solid var(--border);padding:.5rem .7rem;vertical-align:top;}
|
||||
td:not(:last-child),th:not(:last-child){border-inline-end:1px solid var(--border);}
|
||||
tbody tr:last-child td{border-bottom:none;}
|
||||
ul,ol{margin:.6rem 0;padding-inline-start:1.5rem;}
|
||||
li{margin:.35rem 0;}
|
||||
li>code:first-child{font-weight:600;}
|
||||
nav.toc{background:var(--panel);border:1px solid var(--border);border-radius:10px;
|
||||
padding:1rem 1.3rem;margin:1.4rem 0 2rem;font-size:.92rem;}
|
||||
nav.toc strong{display:block;margin-bottom:.4rem;}
|
||||
nav.toc ul{margin:.2rem 0;padding-inline-start:1.1rem;list-style:none;}
|
||||
nav.toc>ul{padding-inline-start:0;}
|
||||
nav.toc li{margin:.2rem 0;}
|
||||
nav.toc .l3{padding-inline-start:1.1rem;font-size:.88em;color:var(--muted);}
|
||||
nav.toc .l3 a{color:var(--muted);}
|
||||
.crumbs{font-size:.85rem;color:var(--muted);margin-bottom:.3rem;}
|
||||
.crumbs a{color:var(--muted);}
|
||||
.stamp{font-size:.85rem;color:var(--muted);margin:-.4rem 0 1rem;}
|
||||
figure.diagram{margin:1.5rem 0;padding:1rem;background:var(--panel);
|
||||
border:1px solid var(--border);border-radius:10px;overflow-x:auto;}
|
||||
figure.diagram svg{display:block;min-width:900px;width:100%;height:auto;}
|
||||
details.src{margin:.6rem 0 1.6rem;font-size:.85rem;color:var(--muted);}
|
||||
details.src summary{cursor:pointer;}
|
||||
svg text{fill:var(--fg);font:13px ui-sans-serif,system-ui,"Segoe UI",sans-serif;}
|
||||
svg .t2{font-size:11px;fill:var(--muted);}
|
||||
svg .grp-title{font-size:12px;font-weight:600;fill:var(--muted);letter-spacing:.04em;}
|
||||
svg .box{fill:var(--panel);stroke:var(--fg);stroke-opacity:.55;rx:8;}
|
||||
svg .box.live{stroke:var(--accent);stroke-opacity:1;stroke-width:1.6;}
|
||||
svg .box.mock{stroke-dasharray:5 4;}
|
||||
svg .grp{fill:none;stroke:var(--border);stroke-width:1.2;rx:12;}
|
||||
svg .edge{fill:none;stroke-width:1.7;}
|
||||
svg .edge.real{stroke:var(--accent);}
|
||||
svg .edge.mock{stroke:var(--muted);stroke-dasharray:6 4;}
|
||||
svg .lbl{font-size:10.5px;fill:var(--muted);}
|
||||
svg .arr-real{fill:var(--accent);} svg .arr-mock{fill:var(--muted);}
|
||||
</style>
|
||||
</head>
|
||||
<body><main>
|
||||
<div class="crumbs"><a href="index.html">← Post-phase server audit</a></div>
|
||||
<h1 id="runtime-services-deployment-topology">Runtime services — deployment topology</h1><p class="stamp">Generated from the canonical Markdown — do not hand-edit. Audit date 2026-07-10.</p><nav class="toc"><strong>Contents</strong><ul><li class="l2"><a href="#service-inventory">Service inventory</a></li><li class="l2"><a href="#dependency-graph">Dependency graph</a></li><li class="l2"><a href="#per-service-notes">Per-service notes</a></li><li class="l3"><a href="#1-2-sql-server-baya-baya-logs">1–2 · SQL Server (<code>Baya</code> + <code>Baya_Logs</code>)</a></li><li class="l3"><a href="#3-reverse-proxy-tls">3 · Reverse proxy / TLS</a></li><li class="l3"><a href="#4-prometheus-grafana">4 · Prometheus (+ Grafana)</a></li><li class="l3"><a href="#5-redis">5 · Redis</a></li><li class="l3"><a href="#6-minio-s3-arvancloud">6 · MinIO / S3 / ArvanCloud</a></li><li class="l3"><a href="#7-job-scheduler">7 · Job scheduler</a></li><li class="l3"><a href="#8-sms-gateway">8 · SMS gateway</a></li><li class="l3"><a href="#9-psp-ipg-shaparak-تسهیم">9 · PSP / IPG + Shaparak (تسهیم)</a></li><li class="l3"><a href="#10-bnpl-providers">10 · BNPL providers</a></li><li class="l3"><a href="#11-bank-transfer-rail-paya-satna">11 · Bank-transfer rail (PAYA/SATNA)</a></li><li class="l3"><a href="#12-سامانه-مودیان">12 · سامانه مودیان</a></li><li class="l3"><a href="#13-kyc-bridge-shahkar-e-kyc-استعلام-شبا">13 · KYC bridge (Shahkar / e-KYC / استعلام شبا)</a></li><li class="l3"><a href="#14-geocoding-neshan">14 · Geocoding (Neshan)</a></li><li class="l3"><a href="#15-review-moderation">15 · Review moderation</a></li><li class="l3"><a href="#16-moh-ino-enamad-a-process-not-a-service">16 · MoH / INO / eNamad — a process, not a service</a></li><li class="l3"><a href="#17-elasticsearch-deliberately-later">17 · Elasticsearch — deliberately later</a></li><li class="l2"><a href="#deployment-notes-from-the-code-not-aspiration">Deployment notes (from the code, not aspiration)</a></li></ul></nav>
|
||||
<p><strong>Audit date:</strong> 2026-07-10 · <strong>Derivation:</strong> every entry below is justified from the code — the DI seam that depends on it, the config key that names it, or the package/startup wiring that talks to it. Nothing is invented; "not needed" claims are backed by the absence of the package/code. Make-it-real steps live in <code>dev/shared-working-context/reports/mocks-registry.md</code> (row references below).</p>
|
||||
<p><strong>The shape in one sentence:</strong> today the API binary talks to exactly <strong>one external system — SQL Server</strong> (app DB + log DB); everything else (18 seams) is an in-process mock, so "deployment" today is one container + one database — and the table below is the roadmap of what must exist as each seam goes real.</p>
|
||||
<h2 id="service-inventory">Service inventory</h2>
|
||||
<div class="tblwrap"><table><thead><tr><th>#</th><th>Service</th><th>Purpose</th><th>Depends via (seam / config)</th><th>MVP?</th><th>Registry row</th></tr></thead><tbody><tr><td>1</td><td><strong>SQL Server</strong> (app DB <code>Baya</code>)</td><td>System of record — 12 schemas (<code>usr ops geo catalog verif search booking payments payouts reviews messaging partner</code>)</td><td>EF Core; <code>ConnectionStrings:SqlServer</code></td><td><strong>Required now</strong></td><td>—</td></tr><tr><td>2</td><td><strong>SQL Server</strong> (log DB <code>Baya_Logs</code>)</td><td>Serilog sink in deployed envs (Warning+, auto-created <code>log.LogEvents</code>)</td><td><code>ConnectionStrings:logDb</code></td><td><strong>Required now</strong> (deployed)</td><td>—</td></tr><tr><td>3</td><td><strong>Reverse proxy / TLS</strong> (nginx·caddy·traefik)</td><td>TLS termination, HTTP/1.1+2, forwarded headers</td><td>Kestrel config; JWE bearer</td><td><strong>Required now</strong></td><td>—</td></tr><tr><td>4</td><td><strong>Prometheus</strong> (+ Grafana)</td><td>Scrapes <code>/metrics</code>; health forwarded to gauges</td><td><code>UseMetricServer</code> + OTel exporter</td><td><strong>Recommended now</strong></td><td>—</td></tr><tr><td>5</td><td><strong>Redis</strong></td><td><code>ICacheService</code> + <code>IDistributedLock</code> (money-path mutex)</td><td><code>Seams:*</code> (keys TBD; none today)</td><td>Before >1 API instance</td><td>rows 14, 42</td></tr><tr><td>6</td><td><strong>MinIO / S3 / ArvanCloud</strong></td><td><code>IObjectStorage</code> — verification docs, avatars (REQ-006), invoice PDFs</td><td><code>Seams:ObjectStorage:*</code></td><td>Before real verification</td><td>row 13</td></tr><tr><td>7</td><td><strong>Job scheduler</strong> (Hangfire/Quartz, in-app on SQL)</td><td>The deferred crons: payout batch, expiry scan, no-show, Moadian poll</td><td>hosted services (no interface exists)</td><td>Before unattended ops</td><td>row 26</td></tr><tr><td>8</td><td><strong>SMS gateway</strong> (Kavenegar·Ghasedak·SMS.ir)</td><td><code>ISmsSender</code> — OTP delivery (login is impossible without it)</td><td><code>Seams:Sms:*</code> (to be added)</td><td><strong>Launch-critical</strong></td><td>row 12</td></tr><tr><td>9</td><td><strong>PSP / IPG + Shaparak</strong> (ZarinPal·Sadad·Vandar·Jibit)</td><td><code>IPaymentProvider</code> + <code>IWebhookVerifier</code> + <code>ISettlementSplitProvider</code> (تسهیم)</td><td>encrypted <code>payment_gateways.config_json</code></td><td>Real payments</td><td>rows 39–41</td></tr><tr><td>10</td><td><strong>BNPL providers</strong> (SnappPay·Digipay)</td><td><code>IBnplProvider</code> / <code>IBnplProviderResolver</code> / <code>ICurrencyNormalizer</code></td><td><code>Seams:Bnpl:*</code>, <code>Seams:Currency:*</code>, gateway config</td><td>Optional at launch</td><td>rows 46–47</td></tr><tr><td>11</td><td><strong>Bank-transfer rail</strong> (Jibit·Vandar·Sadad payout API → PAYA/SATNA)</td><td><code>IBankTransferProvider</code> — weekly nurse payouts</td><td><code>Seams:BankTransfer:*</code></td><td>Real payouts</td><td>row 23</td></tr><tr><td>12</td><td><strong>سامانه مودیان</strong> (tax e-invoicing)</td><td><code>IMoadianClient</code> — legal invoice registration</td><td><code>Seams:Moadian:*</code> + signing cert</td><td>Legal — soon after launch</td><td>row 45</td></tr><tr><td>13</td><td><strong>KYC bridge vendor</strong> (Finnotech-class)</td><td><code>IShahkarVerifier</code> + <code>IIdentityKycProvider</code> + <code>IBankAccountOwnershipVerifier</code></td><td><code>Seams:Shahkar:*</code>, <code>Seams:IdentityKyc:*</code>, <code>Seams:BankOwnership:*</code></td><td>Real verification + payout gate</td><td>rows 27, 28, 30</td></tr><tr><td>14</td><td><strong>Geocoding</strong> (Neshan)</td><td><code>IGeocoder</code> — address → coordinates (EVV distance)</td><td><code>Seams:Geocoding:*</code></td><td>With real EVV</td><td>row 31</td></tr><tr><td>15</td><td><strong>Review-moderation classifier</strong> (LLM/API)</td><td><code>IReviewModerationService</code> — AI pre-screen</td><td><code>Seams:ReviewModeration:*</code></td><td>Optional (human queue is default)</td><td>row 33</td></tr><tr><td>16</td><td><strong>MoH / INO / eNamad</strong></td><td><code>ICredentialVerifier</code> + <code>ILicenseVerificationService</code> — <strong>manual admin process; no public B2B API exists</strong></td><td><code>Seams:LicenseVerification:*</code></td><td>Manual = the MVP design</td><td>rows 29, 50</td></tr><tr><td>17</td><td><strong>Elasticsearch</strong></td><td><code>INurseSearch</code> alt backend + outbox feeder</td><td><code>Search:Backend</code> (non-<code>sql</code> throws today)</td><td><strong>Not MVP</strong> — SQL search is real</td><td>rows 38, 43</td></tr></tbody></table></div>
|
||||
<p><strong>Explicitly not needed</strong> (verified absent from <code>server/Directory.Packages.props</code> and code): message broker (no RabbitMQ/Kafka), Redis today (no <code>StackExchange.Redis</code>), Hangfire/Quartz today, Elasticsearch client (<code>Elastic.Clients.*</code>), any cloud SDK, any payment/SMS vendor SDK. The only externally-pointing package beyond SQL Server is <code>Serilog.Sinks.Elasticsearch</code> — its wiring is commented out (<code>Baya.Infrastructure.CrossCutting/Logging/LoggingConfiguration.cs:58-70</code>).</p>
|
||||
<hr>
|
||||
<h2 id="dependency-graph">Dependency graph</h2>
|
||||
<p>Solid edges are live today; dashed edges are behind a mocked seam (the arrow exists in code, the wire does not). One line per node below the graph.</p>
|
||||
<figure class="diagram"><svg viewBox="0 0 1160 830" role="img" aria-label="Balinyaar runtime dependency graph">
|
||||
<defs>
|
||||
<marker id="arr-real" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="arr-real" d="M0,0 L10,5 L0,10 z"/></marker>
|
||||
<marker id="arr-mock" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path class="arr-mock" d="M0,0 L10,5 L0,10 z"/></marker>
|
||||
</defs>
|
||||
|
||||
<rect class="box live" x="20" y="150" width="210" height="62" rx="8"/><text x="125" y="178" text-anchor="middle" font-weight="600">Next.js client</text><text class="t2" x="125" y="195" text-anchor="middle">HTTPS · JSON (camelCase)</text>
|
||||
<rect class="box live" x="20" y="430" width="210" height="62" rx="8"/><text x="125" y="458" text-anchor="middle" font-weight="600">Prometheus (+ Grafana)</text><text class="t2" x="125" y="475" text-anchor="middle">scrapes /metrics + health</text>
|
||||
|
||||
<rect class="grp" x="290" y="90" width="270" height="330" rx="12"/>
|
||||
<text class="grp-title" x="425" y="114" text-anchor="middle">API HOST · Baya.Web.Api (:5002)</text>
|
||||
<rect class="box live" x="310" y="132" width="230" height="78" rx="8"/><text x="425" y="150" text-anchor="middle" font-weight="600">ASP.NET Core API</text><text class="t2" x="425" y="166" text-anchor="middle">REST /api/v1 · Swagger</text><text class="t2" x="425" y="180" text-anchor="middle">/metrics · /HealthCheck · gRPC</text>
|
||||
<rect class="box live" x="310" y="250" width="230" height="62" rx="8"/><text x="425" y="278" text-anchor="middle" font-weight="600">In-proc interval jobs</text><text class="t2" x="425" y="295" text-anchor="middle">expiry 1min · retention 24h</text>
|
||||
<path class="edge real" d="M 425 250 L 425 210"/>
|
||||
<text class="t2" x="425" y="345" text-anchor="middle">18 vendor seams resolve</text>
|
||||
<text class="t2" x="425" y="361" text-anchor="middle">in-process (mocks) today —</text>
|
||||
<text class="t2" x="425" y="377" text-anchor="middle">dashed edges = the wire to build</text>
|
||||
|
||||
<rect class="grp" x="640" y="20" width="500" height="216" rx="12"/>
|
||||
<text class="grp-title" x="660" y="44">DATA & PLATFORM INFRA</text>
|
||||
<rect class="box live" x="660" y="58" width="225" height="62" rx="8"/><text x="772.5" y="86" text-anchor="middle" font-weight="600">SQL Server «Baya»</text><text class="t2" x="772.5" y="103" text-anchor="middle">12 schemas · system of record</text>
|
||||
<rect class="box live" x="900" y="58" width="220" height="62" rx="8"/><text x="1010" y="86" text-anchor="middle" font-weight="600">SQL Server «Baya_Logs»</text><text class="t2" x="1010" y="103" text-anchor="middle">Serilog sink (deployed envs)</text>
|
||||
<rect class="box mock" x="660" y="146" width="225" height="62" rx="8"/><text x="772.5" y="174" text-anchor="middle" font-weight="600">Redis</text><text class="t2" x="772.5" y="191" text-anchor="middle">cache + dist. lock — in-proc now</text>
|
||||
<rect class="box mock" x="900" y="146" width="220" height="62" rx="8"/><text x="1010" y="174" text-anchor="middle" font-weight="600">MinIO / S3</text><text class="t2" x="1010" y="191" text-anchor="middle">object storage — local disk now</text>
|
||||
|
||||
<rect class="grp" x="640" y="260" width="500" height="216" rx="12"/>
|
||||
<text class="grp-title" x="660" y="284">MONEY RAILS (ALL MOCKED TODAY)</text>
|
||||
<rect class="box mock" x="660" y="298" width="225" height="62" rx="8"/><text x="772.5" y="326" text-anchor="middle" font-weight="600">PSP / IPG + Shaparak</text><text class="t2" x="772.5" y="343" text-anchor="middle">capture · webhook · تسهیم</text>
|
||||
<rect class="box mock" x="900" y="298" width="220" height="62" rx="8"/><text x="1010" y="326" text-anchor="middle" font-weight="600">BNPL</text><text class="t2" x="1010" y="343" text-anchor="middle">SnappPay / Digipay</text>
|
||||
<rect class="box mock" x="660" y="386" width="225" height="62" rx="8"/><text x="772.5" y="414" text-anchor="middle" font-weight="600">PAYA / SATNA rail</text><text class="t2" x="772.5" y="431" text-anchor="middle">weekly nurse payouts</text>
|
||||
<rect class="box mock" x="900" y="386" width="220" height="62" rx="8"/><text x="1010" y="414" text-anchor="middle" font-weight="600">سامانه مودیان</text><text class="t2" x="1010" y="431" text-anchor="middle">tax e-invoicing</text>
|
||||
|
||||
<rect class="grp" x="640" y="500" width="500" height="300" rx="12"/>
|
||||
<text class="grp-title" x="660" y="524">TRUST & IDENTITY RAILS (ALL MOCKED TODAY)</text>
|
||||
<rect class="box mock" x="660" y="538" width="225" height="62" rx="8"/><text x="772.5" y="566" text-anchor="middle" font-weight="600">SMS gateway (OTP)</text><text class="t2" x="772.5" y="583" text-anchor="middle">launch-critical — logs only now</text>
|
||||
<rect class="box mock" x="900" y="538" width="220" height="62" rx="8"/><text x="1010" y="566" text-anchor="middle" font-weight="600">KYC bridge</text><text class="t2" x="1010" y="583" text-anchor="middle">Shahkar · e-KYC · استعلام شبا</text>
|
||||
<rect class="box mock" x="660" y="626" width="225" height="62" rx="8"/><text x="772.5" y="654" text-anchor="middle" font-weight="600">Neshan geocoding</text><text class="t2" x="772.5" y="671" text-anchor="middle">EVV distance accuracy</text>
|
||||
<rect class="box mock" x="900" y="626" width="220" height="62" rx="8"/><text x="1010" y="654" text-anchor="middle" font-weight="600">Review moderation</text><text class="t2" x="1010" y="671" text-anchor="middle">optional — human queue default</text>
|
||||
<rect class="box mock" x="660" y="714" width="460" height="62" rx="8"/><text x="890" y="742" text-anchor="middle" font-weight="600">MoH / INO / eNamad</text><text class="t2" x="890" y="759" text-anchor="middle">manual admin review — no public API exists</text>
|
||||
|
||||
<path class="edge real" d="M 230 181 C 270 181, 270 171, 310 171" marker-end="url(#arr-real)"/><text class="lbl" x="270" y="170" text-anchor="middle">HTTPS/JSON</text>
|
||||
<path class="edge real" d="M 230 455 C 260 455, 260 400, 290 400" marker-end="url(#arr-real)"/><text class="lbl" x="260" y="419.5" text-anchor="middle">scrape</text>
|
||||
<path class="edge real" d="M 560 130 C 600 130, 600 100, 640 100" marker-end="url(#arr-real)"/><text class="lbl" x="600" y="99" text-anchor="middle">EF Core · Serilog</text>
|
||||
<path class="edge mock" d="M 560 170 C 600 170, 600 185, 640 185" marker-end="url(#arr-mock)"/><text class="lbl" x="600" y="193.5" text-anchor="middle">cache · lock · files</text>
|
||||
<path class="edge mock" d="M 560 245 C 600 245, 600 320, 640 320" marker-end="url(#arr-mock)"/><text class="lbl" x="600" y="268.5" text-anchor="middle">capture · payouts · invoices</text>
|
||||
<path class="edge mock" d="M 640 395 C 600 395, 600 290, 560 290" marker-end="url(#arr-mock)"/><text class="lbl" x="600" y="356.5" text-anchor="middle">webhooks / callbacks</text>
|
||||
<path class="edge mock" d="M 560 335 C 600 335, 600 610, 640 610" marker-end="url(#arr-mock)"/><text class="lbl" x="600" y="466.5" text-anchor="middle">OTP · استعلام · geocode · moderation</text>
|
||||
|
||||
<rect class="grp" x="20" y="600" width="210" height="130" rx="12"/>
|
||||
<text class="grp-title" x="125" y="624" text-anchor="middle">LEGEND</text>
|
||||
<path class="edge real" d="M 40 646 L 90 646"/><text class="t2" x="100" y="650">live today</text>
|
||||
<path class="edge mock" d="M 40 674 L 90 674"/><text class="t2" x="100" y="678">mocked seam — to build</text>
|
||||
<rect class="box mock" x="40" y="694" width="50" height="20" rx="6"/><text class="t2" x="100" y="708">service to provision</text>
|
||||
</svg></figure>
|
||||
<details class="src"><summary>Mermaid source (canonical, in the .md)</summary><pre><code>flowchart LR
|
||||
subgraph fe[Frontend]
|
||||
WEB["Next.js client"]
|
||||
end
|
||||
subgraph host["API host — Baya.Web.Api (:5002)"]
|
||||
API["ASP.NET Core API\nREST /api/v1 · /metrics · /HealthCheck"]
|
||||
JOBS["In-proc interval jobs\n(→ Hangfire/Quartz later)"]
|
||||
end
|
||||
subgraph data[Data & platform infra]
|
||||
SQL[("SQL Server 'Baya'\n12 schemas · migrations on boot")]
|
||||
LOG[("SQL Server 'Baya_Logs'\nSerilog sink, Warning+")]
|
||||
REDIS[("Redis — cache + dist. lock\n(in-proc today)")]
|
||||
S3[("MinIO / S3 — object storage\n(local disk today)")]
|
||||
end
|
||||
subgraph money["Money rails (all mocked today)"]
|
||||
PSP["PSP / IPG + Shaparak\ncapture · webhook · تسهیم"]
|
||||
BNPL["BNPL — SnappPay / Digipay"]
|
||||
BANK["PAYA / SATNA payout rail"]
|
||||
MOAD["سامانه مودیان e-invoicing"]
|
||||
end
|
||||
subgraph trust["Trust & identity rails (all mocked today)"]
|
||||
SMS["SMS gateway (OTP)"]
|
||||
KYC["Shahkar · e-KYC · استعلام شبا"]
|
||||
GEOC["Neshan geocoding"]
|
||||
MODAI["Review-moderation classifier"]
|
||||
MANUAL["MoH / INO / eNamad\n(manual admin review)"]
|
||||
end
|
||||
subgraph obs[Observability]
|
||||
PROM["Prometheus (+ Grafana)"]
|
||||
end
|
||||
WEB -->|HTTPS/JSON| API
|
||||
API --> SQL
|
||||
API --> LOG
|
||||
JOBS --- API
|
||||
API -.-> REDIS
|
||||
API -.-> S3
|
||||
API -.-> SMS
|
||||
API <-.-> PSP
|
||||
API <-.-> BNPL
|
||||
API -.-> BANK
|
||||
API -.-> MOAD
|
||||
API -.-> KYC
|
||||
API -.-> GEOC
|
||||
API -.-> MODAI
|
||||
MANUAL -.- API
|
||||
PROM -->|scrape /metrics| API</code></pre></details>
|
||||
<ul><li><strong>Next.js client</strong> — the only API consumer; reads <code>NEXT_PUBLIC_API_URL</code> (root <code>CLAUDE.md</code>).</li><li><strong>API</strong> — single ASP.NET Core host; all seams resolve in-process today.</li><li><strong>In-proc jobs</strong> — the two <code>BackgroundService</code> sweeps (booking-request expiry 1 min, notification retention 24 h); the scheduler upgrade re-homes them (plan §4.1).</li><li><strong>SQL Server <code>Baya</code></strong> — system of record; migrations + seeding run on every non-Testing boot.</li><li><strong>SQL Server <code>Baya_Logs</code></strong> — deployed-env Serilog sink (auto-creates DB/table).</li><li><strong>Redis</strong> — target for <code>ICacheService</code>/<code>IDistributedLock</code>; nothing speaks Redis yet.</li><li><strong>MinIO/S3</strong> — target for <code>IObjectStorage</code>; local disk + <code>file://</code> URLs today.</li><li><strong>PSP/IPG</strong> — card capture, callback signatures, تسهیم settlement split.</li><li><strong>BNPL</strong> — provider-financed installments; settle/revert callbacks.</li><li><strong>PAYA/SATNA</strong> — weekly nurse payout batches + async reconciliation.</li><li><strong>مودیان</strong> — legal e-invoice registration (pending→registered poll).</li><li><strong>KYC vendor</strong> — Shahkar phone↔NID, identity+liveness, Sheba ownership (payout gate).</li><li><strong>Neshan</strong> — geocoding for address coordinates / EVV distance.</li><li><strong>Moderation classifier</strong> — optional AI pre-screen; human moderation is the default gate.</li><li><strong>MoH/INO/eNamad</strong> — human verification workflows, by design (no API exists).</li><li><strong>Prometheus</strong> — scrapes <code>/metrics</code>; health check results forwarded as gauges.</li></ul>
|
||||
<hr>
|
||||
<h2 id="per-service-notes">Per-service notes</h2>
|
||||
<h3 id="1-2-sql-server-baya-baya-logs">1–2 · SQL Server (<code>Baya</code> + <code>Baya_Logs</code>)</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>UseSqlServer</code> at <code>server/src/Infrastructure/Baya.Infrastructure.Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs:41</code>; sink at <code>…CrossCutting/Logging/LoggingConfiguration.cs:44-45</code> (schema <code>log</code>, auto-create); the 12 schemas via per-entity <code>ToTable(name, schema)</code> (e.g. <code>PaymentsConfig/LedgerEntryConfig.cs:18</code>, <code>PayoutsConfig/NursePayoutConfig.cs:19</code>).</li><li><strong>Default:</strong> <code>mcr.microsoft.com/mssql/server:2022-latest</code> (Developer for dev; licensed edition in prod). Both DBs fit one instance; <code>Baya_Logs</code> can move later.</li><li><strong>Config:</strong> <code>ConnectionStrings:SqlServer</code>, <code>ConnectionStrings:logDb</code> — <strong>rotate + externalize first</strong> (plan §1.1; live <code>sa</code> credentials are committed today).</li><li><strong>Health/readiness:</strong> the app's only health check (<code>/HealthCheck</code>, <code>Monitoring/Configurations/HealthCheckConfigurations.cs:17</code>); <code>logDb</code> has none (plan §7.2). Boot runs <code>MigrateAsync</code> + 3 seeders (<code>Program.cs:99-104</code>) → the login needs DDL rights and concurrent multi-node boot races (plan §4.3).</li></ul>
|
||||
<h3 id="3-reverse-proxy-tls">3 · Reverse proxy / TLS</h3>
|
||||
<ul><li><strong>Evidence of need:</strong> JWE bearer auth (<code>RequireHttpsMetadata</code> must be true in prod — <code>Identity/ServiceConfiguration/ServiceCollectionExtension.cs:139</code>); Kestrel <code>EndpointDefaults=Http2</code> breaks non-TLS HTTP/1.1 (<code>appsettings.json:29-33</code>, plan §1.5); the rate limiter partitions on <code>RemoteIpAddress</code> with <strong>no ForwardedHeaders middleware</strong> (<code>WebFramework/ServiceConfiguration/RateLimitingServiceExtension.cs:69</code>, plan §1.6) — the proxy must pass <code>X-Forwarded-For</code> <em>and</em> the app must be taught to honor it.</li><li><strong>Default:</strong> caddy 2 / nginx 1.27; terminate TLS, h2 to clients, HTTP/1.1 (or h2c) upstream once §1.5 lands.</li></ul>
|
||||
<h3 id="4-prometheus-grafana">4 · Prometheus (+ Grafana)</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>/metrics</code> via prometheus-net <code>UseMetricServer</code> + OTel <code>AddPrometheusExporter</code> (two stacks — consolidate, plan §7.1) at <code>Monitoring/Configurations/PrometheusMetricsConfigurations.cs:11</code> and <code>OpenTelemetryConfigurations.cs:21</code>; health forwarded (<code>HealthCheckConfigurations.cs:18</code>).</li><li><strong>Default:</strong> <code>prom/prometheus:v2.53</code> + <code>grafana/grafana:11</code>. No tracing backend exists yet (metrics-only); an OTLP collector becomes relevant with plan §7.1.</li></ul>
|
||||
<h3 id="5-redis">5 · Redis</h3>
|
||||
<ul><li><strong>Evidence of the gap:</strong> <code>MemoryCacheService</code> and <code>InProcessDistributedLock</code> (<code>CrossCutting/Seams/MemoryCacheService.cs:11</code>, <code>InProcessDistributedLock.cs:14</code>) — single-process only; the money-path lock convention <code>booking:{id}:payment|refund</code> is already in the handlers.</li><li><strong>Default:</strong> <code>redis:7-alpine</code> (AOF on). <strong>Required the moment a second API instance runs</strong> (shared cache invalidation generation-tokens + cross-instance money mutex). Config keys to be introduced with the swap (plan §4.2); none exist today.</li></ul>
|
||||
<h3 id="6-minio-s3-arvancloud">6 · MinIO / S3 / ArvanCloud</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>LocalDiskObjectStorage</code> writes under a temp root and returns <code>file://</code> URLs (<code>CrossCutting/Seams/LocalDiskObjectStorage.cs:20,56</code>); consumers: b6 verification documents (signed-URL upload flow), future avatars (REQ-006), invoice <code>PdfStorageKey</code> (<code>InvoicesConfig/InvoiceConfig.cs</code> — <code>pdf_storage_key</code> column).</li><li><strong>Default:</strong> <code>minio/minio:latest</code> (S3-compatible; ArvanCloud object storage is the Iran-hosted option).</li><li><strong>Config:</strong> <code>Seams:ObjectStorage:RootPath</code> today → bucket/endpoint/keys with the swap. Presigned PUT/GET with expiry is the contract the frontend already codes against.</li></ul>
|
||||
<h3 id="7-job-scheduler">7 · Job scheduler</h3>
|
||||
<ul><li><strong>Evidence:</strong> two <code>PeriodicTimer</code> hosted services only (<code>Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs:63,67</code>); the payout/expiry/no-show/ Moadian crons are admin-manual with seeded-but-unread cadence keys (plan §4.1). <strong>No <code>IJobScheduler</code> interface exists</strong> — the registry name is aspirational.</li><li><strong>Default:</strong> Hangfire on the existing SQL Server (no new container) — dashboard behind admin auth; or Quartz with SQL persistence. Not a separate service to "spin up", but it changes the SQL footprint (schema) and ops (dashboard, retries).</li></ul>
|
||||
<h3 id="8-sms-gateway">8 · SMS gateway</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>LoggingSmsSender</code> logs OTPs instead of sending (<code>CrossCutting/Seams/LoggingSmsSender.cs:16</code>) — <strong>no real user can log in</strong>; per-phone resend window + <code>otp</code> rate policy already enforced upstream.</li><li><strong>Default:</strong> Kavenegar / Ghasedak / SMS.ir (SaaS — API key, no container). Keys to add: <code>Seams:Sms:{ApiKey,SenderLine,BaseUrl}</code> (registry row 12). Template/pattern OTP send for deliverability.</li></ul>
|
||||
<h3 id="9-psp-ipg-shaparak-تسهیم">9 · PSP / IPG + Shaparak (تسهیم)</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>MockPaymentProvider</code> (instant success, <code>VerifyAsync</code> echoes the expected amount — <code>CrossCutting/Seams/MockPaymentProvider.cs:24</code>), <code>MockWebhookVerifier</code> (marker-based "signature" — <code>MockWebhookVerifier.cs:21</code>), <code>MockSettlementSplitProvider</code> (<code>MockSettlementSplitProvider.cs:13</code>). Real merchant credentials belong in the <strong>encrypted</strong> <code>payment_gateways.config_json</code> (seeded sandbox row: <code>Persistence/ServiceCollectionExtensions.cs:109-117</code> — environment-gate it, plan §1.4).</li><li><strong>Default:</strong> SaaS (ZarinPal / Sadad / Vandar / Jibit) — needs merchant + terminal registration and تسهیم (settlement-split) setup to registered IBANs; webhook endpoint is already public (<code>POST webhooks/payments/{provider}</code>) with upsert-first idempotency in place.</li></ul>
|
||||
<h3 id="10-bnpl-providers">10 · BNPL providers</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>MockBnplProvider</code> drives the full verb set + state machine (<code>CrossCutting/Seams/MockBnplProvider.cs:17-53</code>); callback endpoint <code>WebhooksBnplController</code> (signed, rate-limited). <code>Seams:Bnpl:*</code> + <code>Seams:Currency:TomanToIrrMultiplier</code> (<code>SeamOptions.cs:98-120,79</code>).</li><li><strong>Default:</strong> SnappPay and/or Digipay (SaaS, OAuth). <strong>Do plan §2.1 first</strong> — the revert-clearing path is currently unreachable, so real BNPL refunds would strand ledger state.</li></ul>
|
||||
<h3 id="11-bank-transfer-rail-paya-satna">11 · Bank-transfer rail (PAYA/SATNA)</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>MockBankTransferProvider</code> settles every instruction instantly (<code>CrossCutting/Seams/MockBankTransferProvider.cs:18-31</code>); the handler already chooses PAYA vs SATNA by <code>payout_satna_threshold_irr</code> (<code>platform_configs</code> seed row 22) and the irreversibility backstops (unconditional <code>UNIQUE(booking_id)</code> link, forward-only payout machine) are in place.</li><li><strong>Default:</strong> Jibit / Vandar / Sadad payout API (SaaS). Needs the source settlement account + the async <code>submitted → paid/failed</code> reconciliation callback the mock collapses (registry row 23, steps 3–4).</li></ul>
|
||||
<h3 id="12-سامانه-مودیان">12 · سامانه مودیان</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>MockMoadianClient</code> leaves invoices <code>pending</code> forever (<code>CrossCutting/Seams/MockMoadianClient.cs:21</code>); no reconciliation job exists (plan §6.5). VAT-on-commission + sequential invoice numbers are already correct server-side.</li><li><strong>Default:</strong> government SaaS — enrollment (memory/economic code) + a signing certificate; the certificate is a deploy-time secret.</li></ul>
|
||||
<h3 id="13-kyc-bridge-shahkar-e-kyc-استعلام-شبا">13 · KYC bridge (Shahkar / e-KYC / استعلام شبا)</h3>
|
||||
<ul><li><strong>Evidence:</strong> three deterministic mocks with magic-value failure cases (<code>MockShahkarVerifier.cs:26-37</code>, <code>MockIdentityKycProvider.cs:25</code>, <code>MockBankAccountOwnershipVerifier.cs:26</code>); handlers already persist <code>external_response_json</code> and treat shared-SIM as a handled state. The Sheba-ownership result gates first payouts (<code>matched_national_id</code>).</li><li><strong>Default:</strong> one Finnotech-class bridge covers all three استعلامها (SaaS; API keys under <code>Seams:{Shahkar,IdentityKyc,BankOwnership}:*</code>).</li></ul>
|
||||
<h3 id="14-geocoding-neshan">14 · Geocoding (Neshan)</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>MockGeocoder</code> jitters ±5 km around 8 hardcoded centroids (<code>MockGeocoder.cs:52</code>) — EVV distance checks are noise until this (and/or REQ-008's user pin) is real. <code>Seams:Geocoding:*</code> is one of only three seam sections present in <code>appsettings.json</code> (<code>:22-26</code>).</li><li><strong>Default:</strong> Neshan (Iran coverage; SaaS API key), rate-limit/retry per registry row 31.</li></ul>
|
||||
<h3 id="15-review-moderation">15 · Review moderation</h3>
|
||||
<ul><li><strong>Evidence:</strong> keyword-list mock; clean text stays in the human queue by default (<code>MockReviewModerationService.cs:23</code>; <code>Seams:ReviewModeration:*</code>). Human <code>ModerateReviewCommand</code> retains decision authority — so this can stay mocked indefinitely at low volume.</li><li><strong>Default:</strong> any text-moderation API / LLM endpoint when review volume outgrows the human queue.</li></ul>
|
||||
<h3 id="16-moh-ino-enamad-a-process-not-a-service">16 · MoH / INO / eNamad — a process, not a service</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>MockCredentialVerifier</code> always returns <code>RequiresManualReview</code> (<code>MockCredentialVerifier.cs:18</code>); <code>MockLicenseVerificationService</code> likewise (<code>MockLicenseVerificationService.cs:26</code>). The registry itself records <strong>no public B2B API exists</strong> — the admin review queue <em>is</em> the real implementation. Provision: admin staffing + the f15 console.</li></ul>
|
||||
<h3 id="17-elasticsearch-deliberately-later">17 · Elasticsearch — deliberately later</h3>
|
||||
<ul><li><strong>Evidence:</strong> <code>SqlNurseSearch</code> is the real MVP backend; <code>Search:Backend</code> ≠ <code>sql</code> throws at startup (<code>Persistence/ServiceConfiguration/ServiceCollectionExtensions.cs:74-78</code>); no ES client package.</li><li><strong>Default when needed:</strong> <code>elasticsearch:8.x</code> + the outbox feeder (registry rows 38/43; plan §8.1).</li></ul>
|
||||
<hr>
|
||||
<h2 id="deployment-notes-from-the-code-not-aspiration">Deployment notes (from the code, not aspiration)</h2>
|
||||
<ol><li><strong>Boot = migrate + seed.</strong> Every non-Testing start applies EF migrations and seeds roles, the <code>admin</code>/<code>qw123321</code> user, and an <strong>active sandbox ZarinPal gateway</strong> (<code>Program.cs:99-104</code>). Until plan §1.3/§1.4/§4.3 land: single-instance start-up, DDL-privileged login, and clean up the seeded credentials per environment.</li><li><strong>Environment files:</strong> <code>appsettings.json</code> ≡ <code>appsettings.Development.json</code> (byte-identical); <strong>no Production/Staging file exists.</strong> All non-secret env differences ride on ~14 <code>Seams:*</code> groups whose defaults live in code (<code>SeamOptions.cs</code>), not in config files.</li><li><strong>HTTP posture:</strong> HTTP/2-only Kestrel default (plan §1.5), gRPC plugin + reflection always on (plan §7.5), TLS required for JWE sanity.</li><li><strong>Single-instance constraints today:</strong> in-memory cache, in-proc money lock, in-proc sweeps, per-instance rate-limit buckets. Scaling past one instance requires plan §4.2 (Redis) + §4.3 (migrations) first — the DB uniques keep money <em>correct</em> either way, but locks/cache/limits silently degrade.</li><li><strong>Logs:</strong> deployed envs write Warning+ to <code>Baya_Logs</code> only (Information dropped — plan §7.3); dev writes console + <code>logs/log.json</code>.</li><li><strong>Client:</strong> the Next.js app needs <code>NEXT_PUBLIC_API_URL</code> pointing at the proxy; wire casing camelCase; snake_case routes.</li></ol>
|
||||
</main></body></html>
|
||||
Reference in New Issue
Block a user