i
Iris · operare manuală
Operator contabil
Tenant: BONO
OC
Blueprint tehnic

Arhitectura Iris — v2

v2 · 24 August 2026 · încorporează review-ul Prodi + fluxul Nucleului · sursa: iris-arhitectura.md

24 August 2026 · v2 — încorporează review-ul Prodi (24 Aug) + reîncadrarea proiectului · BONO × SOLO · Confidențial Autor: BGN + Claude · Complementar cu prd-iris-mvp-manual.md (spec-ul funcțional al Fazei 1)


0. Contextul v2: un experiment, spus explicit

Iris este și un test de organizație: un modul dezvoltat de BGN + Claude Code, cu ajutorul echipei IT pe arhitectură (acest document e dovada — review-ul Prodi a schimbat v1 în bine), dar fără implicare de implementare sau mentenanță din partea echipei.

Criteriile de decizie derivate din acest obiectiv, aplicate consecvent în tot documentul:

  1. Simplitate — cât mai puține piese mobile; capabilități native ale serviciilor, nu add-on-uri construite de noi
  2. Zero operare umană — totul pe servicii gestionate (managed); nimeni nu e on-call
  3. Reparabil de Claude Code — totul e cod în repo + servicii accesibile prin CLI/API; niciun click-in-consolă nereproductibil
  4. Integrare cu platforma BONO exclusiv prin API — Iris consumă serviciile ecosistemului (iam, directory, forex) prin contracte; nu rulează pe infrastructura echipei și nu-i cere nimic operațional

Ținta de scalare rămâne neschimbată din v1: zeci de mii de clienți finali, sute de mii de documente/lună (~750k/lună ≈ 2–3 doc/sec la vârf), cu plafonul arhitecturii la 10–15× peste țintă.

Cine este „tenantul" — fluxul documentelor (decizia Nucleului, 4 aug 2026, BGN + Prodi)

Iris nu vorbește cu shell-urile (Dublin, Vertigo, Falcon). Per organizație există un singur sistem-partener:

Clientul încarcă în Dublin ──┐
SPV / eFactură ──────────────┤
Facturare (emise) ───────────┼──▶  NUCLEU (BONO)  ──▶ ProcessingRequest ──▶ IRIS
Extrase (upload/OpenBanking)─┘    system of record:                          │
                                   stochează documentele,                    │
                                   registrul firmelor,        ◀── note ──────┘
                                   jurnalul de evenimente
                                        │
                          Dublin și Vertigo citesc din Nucleu

Changelog v1 → v2

Subiectv1v2Sursa deciziei
Stack backendde decis (propus .NET).NET 10, stil minimal modernProdi confirmă; dezbaterea TS vs .NET tranșată pe merite (vezi §1)
Baza de datePostgres (nespecificat)Neon — Postgres gestionat, Frankfurtcriteriul zero-ops; capabilități native (RLS)
Computede decisRailway (containere Linux, UE)criteriul zero-ops; deploy prin CLI
Storage documenteS3-compatibilCloudflare R2 (UE), swap path AWS S3 documentatsimplitate + cont existent; API S3-compatibil
Autentificare tenantchei API în IrisOAuth2 M2M via iam.bono.ro; Iris = resource serverProdi — serviciu existent
ANAF / BNRcache construit în Irisdirectory.bono.ro + forex.bono.ro (existente, cache-uite)Prodi — servicii existente
Criptare documenteenvelope + crypto-shreddingdoar SSE la storage; fără criptare aplicativă în v1Prodi — cost/simplitate; condiție: fereastra de backup în DPA
Nr. afișare BN-YYYY-NNNNNschițămecanica Prodi + decizii: NU gapless; anul = data ingestuluiProdi (mecanică) + BGN (decizii)
HA / failover DBprimary + replicăgestionat de Neon (PITR din bifă); nimic de operatdizolvat de alegerea managed
Bulk importde decismanifest NDJSON + presigned URLs (de validat cu SOLO)propunere BGN, aliniată la reformularea Prodi

1. Stack: .NET 10 minimal + React/TypeScript

Backend: .NET 10 — decizie luată după o dezbatere reală TS vs. .NET, câștigată de .NET pe argumente, nu pe conformism:

Stilul: .NET modern minimal, nu convențiile istorice ale platformei — minimal APIs + EF Core + records + Guid.CreateVersion7(); explicit NU NHibernate/hbm.xml/CQRS ceremonios. Luăm limbajul echipei, nu tot bagajul — zona de maxim corpus pentru Claude Code.

Invariant de dependențe (răspuns la supply chain, valabil și în .NET): lista de pachete externe rămâne scurtă (<15), fixată în versiuni, auditată în CI la fiecare PR.

Frontend: React 19 + TypeScript (Workbench-ul operatorului), cu clientul de API generat din OpenAPI — modelul canonic are o singură sursă de adevăr (C#), tipurile frontend derivă mecanic.

Forma aplicației: monolit modular cu porturi — neschimbat din v1:

Iris (.NET 10, un serviciu + workers)
├── Ingest        — API tenant, validare, dedup hash, triaj
├── Storage       — fișiere (R2), retenție, presigned URLs
├── Workbench API — coada de lucru, conspectare, interpretare, dubii
├── Engines       — porturile IExtraction / IInterpretation / IRules + routerul de cascadă
├── Rules         — taxonomia + tabelul Adam, versionate
├── Memory        — istoricul per (tenant, client, furnizor, descriere)
├── Export        — outbox, webhooks, GET note
├── Identity      — RBAC operatori umani (tenants/chei = iam.bono.ro, NU aici)
└── Telemetry     — evenimente, metrici, cost/doc, SLO

Porturile motoarelor (contracte care rulează din ziua 1, cu implementări manuale în Faza 1) și cele 5 contracte cu regulile lor (schema_version, doar aditiv, confidence nullable, trace_id, fixtures de aur) — neschimbate din v1.


2. Coloana vertebrală: evenimente + proiecții + outbox — neschimbată

Jurnalul de evenimente = sursa adevărului; tabelele de stare = proiecții. Transactional outbox pentru tot ce iese (webhooks semnate, retry + backoff, consum idempotent). Corecțiile post-export = evenimente noi (nota.corrected), notele emise sunt imutabile. Audit record-ul imutabil = evenimente. *(Detalii în v1 — secțiunea rămâne integral valabilă.)*


3. Date: Neon (Postgres gestionat, Frankfurt)

De ce Postgres gestionat, după ce review-ul propusese MariaDB: argumentele MariaDB („echipa o operează", „standardul platformei") nu se aplică unui modul pe care echipa nu îl operează. Sub criteriile §0 câștigă Postgres:

Anexă de compatibilitate MariaDB (din maparea Prodi, păstrată ca plan de portare): mecanica secvențelor, partiționarea RANGE, SKIP LOCKED (10.6+), UUID — toate există și în MariaDB; singura piesă fără echivalent e RLS, compensată exact de stratul aplicativ pe care îl păstrăm oricum. Dacă Iris e predat vreodată echipei și se dorește alinierea la platformă, portarea pierde doar stratul redundant, nu modelul de securitate.

Numărul de afișare BN-2026-00003 — mecanica din review, adoptată integral: rând de secvență (tenant, an) cu SELECT … FOR UPDATE în aceeași tranzacție cu insertul; tranzacție minimă (zero procesare sub lock); alocare după dedup; ordinea lock-urilor consecventă. Deciziile cerute: (1) NU gapless — e handle de afișare/tracking, numerele fiscale reale rămân pe documentele originale; (2) anul = data ingestului — importurile istorice primesc numere în anul curent, în ordinea intrării.

Memoria = tabele normale cu exact-match pe (tenant, client, furnizor_cui, descriere_normalizată) — fără vector DB în v1. Retenție: 12 luni rolling, automată (politici în DB + lifecycle în R2).


4. Compute: Railway (containere Linux, regiunea UE)

API-ul + workerii (coadă, outbox, cron) rulează ca containere .NET pe Railway: deploy dintr-o comandă, loguri/restart/secrete prin CLI — integral operabil de Claude Code. Workbench-ul (SPA React) pe Vercel (fluxul și domeniul există deja). Preview environments: branch de cod (Railway/Vercel) + branch de bază (Neon) = un Iris complet de test per schimbare.

Fluxul, cozile și eșecurile — neschimbate din v1: totul asincron (202 + id instant), idempotență peste tot, fairness per tenant, dead-letter pentru poison documents, circuit breakers. Dependențele externe devin serviciile interne BONO (per review): directory.bono.ro (ANAF, cache-uit) și forex.bono.ro (BNR) — consumate prin porturile ICompanyDirectory / IForexProvider, endpoint-uri în config; indisponibilitate → flag „neverificat", nu blocare. SLO-uri per tenant, măsurate din evenimente, calibrate onest la modelul de operare (natura asincronă iartă: dacă Iris e jos o oră, tenantul retrimite, nimic nu se pierde).

Observabilitate: loguri structurate cu trace_id propagat cap-coadă + un serviciu gestionat de logging (Axiom / Better Stack) — Claude citește logurile prin API când depanează.


5. Storage documente: Cloudflare R2 (UE), cu swap path S3


6. Securitate


7. Economia procesării — neschimbată din v1

Piramida: determinist (parsare + istoric + reguli, cost 0) → Haiku → Sonnet → om (ordinele de mărime: omul e 100× AI-ul scump). Zero-Touch-ul ieftin vine din memorie, nu din inteligență. Lansare cu un singur model (Sonnet); routerul de cascadă există din ziua 1; cascada se activează pe felii, pe datele eval dataset-ului. Shadow mode + feature flags per tenant × categorie = mecanismul de conectare a motoarelor (niciun motor nu primește frâiele direct). Cost/doc = metrică de gradul 1 în Telemetry.


8. Riscurile modelului de operare — spuse onest

  1. Bus factor = BGN. Mitigare: totul în repo (config, migrări, politici — reproductibil), contracte curate, documentele vii pe iris.bgn.ro. Contractele sunt exact ce face posibilă o predare ulterioară către echipă.
  2. Nimeni nu e on-call. SLO-uri oneste + natura asincronă a sistemului (retrimitere idempotentă) + servicii gestionate cu SLA-urile lor.
  3. Cod de securitate scris cu AI. Perimetru minimizat prin design (iam externalizează credențialele, RLS păzește izolarea) + security review-ul punctual cerut la §6.
  4. Divergență de standard (infrastructură proprie într-o casă on-prem). Nu e accident — e ipoteza testului; granița e API-ul, comunicată explicit.

9. Ordinea de execuție — neschimbată ca fazare

  1. Fundația: repo + CI + schema DB (partiționată, RLS, events + outbox) + cele 5 contracte cu fixtures + ingest/export cu tenant fake + coada
  2. Workbench manual (UI-ul prototipat pe iris.bgn.ro/concept-*) + reguli deterministe (tabelul Adam)
  3. Memoria → Descrieri contabile precompletate + fluxul de dubii
  4. Pilotul BONO (documente reale prin Nucleu/Vertigo) + telemetria de cost
  5. Motoarele pe porturi, în shadow mode → precompletare → Zero-Touch pe felii

Pasul 0, înaintea tuturor (concluzia review-ului, adoptată integral): modelul canonic de date — nucleul comun + extensiile per cele 6 tipuri de documente (extrasul de cont = testul polimorfismului), modelul de context (master data), contractele derivate din el. Draft: BGN + Claude; sesiune de shaping cu Prodi + Cristi pe strawman.


10. Statusul deciziilor

Închise în v2: stack (.NET 10 minimal) · DB (Neon Postgres) · compute (Railway) · storage (R2 + swap path) · auth (iam) · ANAF/BNR (directory/forex) · criptare v1 (SSE only) · numere de afișare (mecanică + nu-gapless + an ingest) · contract Tenant-out (Iris definește, destinatarii implementează) · HA (gestionat).

Rămase deschise: formatul final bulk import — manifest NDJSON + presigned ca propunere principală, de validat cu SOLO · numărul concret al ferestrei de backup pentru DPA (propunere: 30 zile) · security review-ul punctual pe perimetru (cerere către Prodi) · modelul canonic de date (pasul 0, în lucru).