ADR-002 — Dove vive il dominio del verticale
Stato: accettata · Proposta il: 15 agosto 2026 · Accettata il: 15 agosto 2026 · Prerequisito di: Fase 1
Contesto
La Fase 1 chiede di «riorganizzare il service nei moduli workforce,
projects, actuals, forecasting, data-exchange, reporting». La parola
service è ambigua, e l'ambiguità non è accademica: i sei moduli target oggi
sono divisi fra due repository, e la divisione taglia i moduli invece di
separarli.
Classificando le 144 funzioni di
data-service/routes/rendicontazione.js per modulo di destinazione:
| Modulo | In backbone-core | Nel verticale |
|---|---|---|
workforce | 520 righe | solo passthrough HTTP |
projects | 299 righe | solo passthrough HTTP |
actuals | 367 righe | 293 (parser presenze) |
forecasting | 404 + 1.205 (allocationEngine) | 29 (delega) |
data-exchange | 815 righe | 986 parser + 498 orchestrazione |
reporting | 55 righe | 451 writer xlsx |
Cinque moduli su sei hanno le regole di business in backbone-core. Il repo verticale possiede i formati — leggere Excel, scrivere xlsx — e l'orchestrazione HTTP: 1.437 righe di logica di formato contro 4.134 di regole di dominio dall'altra parte.
Due elementi rendono la situazione un'anomalia e non una scelta:
AGENTS.mddescrivedata-servicecome «frontend-facing business data API» che estrae louserIddal token e applica i filtri di ownership: un'API dati autorizzata, non un motore di dominio.- Rendicontazione è l'unico verticale con una directory
modules/dentro data-service; il suo file di route è il 17% di tutte le route del servizio e 3,5 volte l'intera impronta di Bandi.
In direzione opposta tira una decisione già presa nel codice:
importRepository.js vieta l'accesso diretto al database dal verticale,
sollevando directDbAccessRemoved su ogni metodo non implementato.
Decisione
Il dominio si sposta nel verticale. data-service resta il layer di
sicurezza e persistenza tenant-aware.
Le due forze non sono in conflitto, perché riguardano cose diverse: il divieto
di accesso diretto al database parla di persistenza, non di regole di
business. Il verticale continua a non parlare mai con datahub; cambia la
grana con cui interroga data-service.
| Responsabilità | Dove |
|---|---|
| Risoluzione organizzazione, membership, capability | data-service |
| CRUD tenant-scoped sulle tabelle del verticale | data-service |
Scrittura di audit_log | data-service |
| Regole di dominio, calcoli, validazioni, formati | rendicontazione-service |
Il contratto passa da granularità caso d'uso a granularità repository: 33 endpoint che incorporano regole diventano 7 endpoint che non ne incorporano nessuna.
Conseguenze
Il prezzo, messo a preventivo. Oggi buildProjectDetail esegue una decina
di letture dentro data-service, a un round trip da datahub. Spostando la
funzione nel verticale quelle letture diventano round trip aggiuntivi fra
container. Serve un endpoint POST /rendicontazione/query che esegua letture
multiple tenant-scoped in una sola richiesta. Senza, il rimpatrio funziona ma
degrada in modo evidente sulle pagine di dettaglio: la latenza p95 va
misurata prima e dopo ogni onda, con una soglia concordata in anticipo.
L'audit cambia forma. Oggi deduce l'azione dal path HTTP di
data-service; con il CRUD generico vedrebbe solo «scrittura su tabella X».
Il verticale dichiarerà l'evento (EmployeeUpdated, PeriodDelivered) e
data-service resterà l'unico a scriverlo, con organizzazione e attore presi
dal token.
Le capability si dividono. data-service mantiene l'enforcement
grossolano e il tenant scope, che restano non aggirabili; il significato fine
dei permessi diventa del verticale. Il verticale non può ampliare i propri
poteri, solo restringerli.
Non si riapre la Fase 0. data-service è oggi l'unico posto dove esiste
l'OrganizationContext. Questa decisione lo lascia dov'è: il confine di tenant
appena chiuso non viene toccato.
Alternative considerate
A — Il dominio resta in data-service e i moduli si creano lì. Nessuna migrazione, ma il confine del repo verticale diventa cosmetico e backbone-core finisce per ospitare il dominio di N verticali. Il file cresce già oggi oltre 3.500 righe.
B — Tutto nel verticale, database compreso. Richiederebbe di ridare l'accesso diretto al database, ribaltando una decisione esplicita e costringendo a ricostruire nel verticale il confine di tenant appena completato.
Attuazione
Strangler in sei onde, descritte nel piano di rimpatrio. A ogni onda i vecchi endpoint restano vivi finché il nuovo percorso non è verde sui golden test; nessuna onda spegne qualcosa prima dell'ultima.
Prerequisito non negoziabile: i golden test di caratterizzazione. Sulle 3.853 righe da spostare la copertura è oggi zero; spostarle senza rete significa riscrivere il comportamento a memoria e scoprire le differenze in produzione.
Approvazione
| Campo | Valore |
|---|---|
| Approvata da | Vincenzo Esposito |
| Data | 15 agosto 2026 |
| Motivazione | data-service non deve contenere logica implementativa, ma solo applicare il layer di sicurezza. La logica applicativa resta nel verticale. |
| Riserve | Nessuna. |
Soglia di latenza — deliberatamente non fissata
Le Conseguenze prevedevano una soglia p95 «concordata in anticipo». All'approvazione si è deciso di non fissarla e di non misurare una baseline: il costo atteso di un salto di rete in più non giustifica il lavoro di misura preventiva.
Conseguenza accettata: se un rallentamento emergerà, non esisterà un
riferimento pre-migrazione con cui confrontarlo, e la diagnosi partirà dai
sintomi. La contromisura strutturale — POST /rendicontazione/query, che
raggruppa le letture multiple in una richiesta sola — è comunque già
implementata, quindi il caso peggiore non è il fan-out ingenuo.
Da qui in avanti la scelta è vincolante: cambiarla richiede un nuovo ADR che superi questo, non una modifica a questa pagina.