Skip to main content

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:

ModuloIn backbone-coreNel verticale
workforce520 righesolo passthrough HTTP
projects299 righesolo passthrough HTTP
actuals367 righe293 (parser presenze)
forecasting404 + 1.205 (allocationEngine)29 (delega)
data-exchange815 righe986 parser + 498 orchestrazione
reporting55 righe451 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.md descrive data-service come «frontend-facing business data API» che estrae lo userId dal 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, capabilitydata-service
CRUD tenant-scoped sulle tabelle del verticaledata-service
Scrittura di audit_logdata-service
Regole di dominio, calcoli, validazioni, formatirendicontazione-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

CampoValore
Approvata daVincenzo Esposito
Data15 agosto 2026
Motivazionedata-service non deve contenere logica implementativa, ma solo applicare il layer di sicurezza. La logica applicativa resta nel verticale.
RiserveNessuna.

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.