Decisioni architetturali prima di generare codice

Lino genera rapidamente molto codice, ma la qualità del risultato dipende dalle decisioni prese prima della generazione. La scelta dei limiti, del tipo di progetto, della strategia di comunicazione e del livello accettabile di debito tecnico impedisce che la produttività iniziale si trasformi in rilavorazione.

Utilizza questa pagina come elenco di controllo dell'architettura per allineare il team, il dominio e le operazioni prima di creare servizi, moduli e funzionalità avanzati.

Tipo di progetto

Prima di correre lino project new, definire l'obiettivo del sistema. Un progetto amministrativo interno, uno SaaS multi-tenant, uno API pubblico e una piattaforma distribuita hanno esigenze diverse.

  • Applicazione semplice: pochi contesti, unico dispiegamento e scarsa necessità di indipendenza operativa.
  • Monolite modulare: diversi moduli nello stesso processo, con limiti espliciti e costi operativi inferiori.
  • Microservizi: servizi separati, implementazione indipendente, comunicazione asincrona e maggiore complessità operativa.
  • SaaS: richiede l'isolamento dell'inquilino, una forte sicurezza, secrets, limitazione della velocità, osservabilità e decisioni chiare su fatturazione/operazioni.

Questa decisione definisce anche quali preoccupazioni dovrebbero sorgere il primo giorno. Uno SaaS non può trattare l'inquilino come un dettaglio in ritardo; uno API pubblico non può nascere senza limiti di velocità e contratti chiari; una soluzione con eventi deve pensare a Outbox, idempotenza e osservabilità; un'applicazione con più culture dovrebbe evitare stringhe sparse fin dall'inizio.

Quando si crea la base, registrare le scelte principali: culture supportate, cache distribuita, comunicazione asincrona, analizzatori di codice, tipo di database, presenza di app Web, autenticazione, tenant e lavoratore. Queste opzioni influiscono su pacchetti, templates, AppHost, parametri, secrets, progetti generati e sullo sforzo richiesto per modificare l'architettura in un secondo momento.

Microservizi modulari e microservizi modulari monolitici

Il monolite modulare consente di organizzare il dominio in moduli con contratti interni chiari, semplificando la creazione, il debug e la distribuzione. I microservizi ripagano solo quando esistono ragioni valide: scala indipendente, team separati, confini aziendali stabili o requisiti operativi diversi.

Partire da un monolite modulare non significa ignorare l’architettura. Significa preservare un semplice deploy mentre il dominio è ancora in fase di scoperta, ma creare dei limiti interni che impediscano che tutto diventi un unico ammasso di entità, servizi e tabelle condivise. Questa è la differenza tra “iniziare in modo semplice” e “iniziare senza struttura”.

CriterioMonolite modulareMicroservizi
DistribuirePiù semplice, solitamente unicoIndipendente per servizio
CoerenzaÈ più facile mantenere le transazioni localiRichiede eventi, Outbox ed eventuale coerenza
OperazioneCosto infrastrutturale inferioreMaggiore osservabilità, code, retry e automazione
RefactoringPiù economico all'inizioPiù difficile quando i contratti sono già stati pubblicati

Lino supporta entrambi i percorsi. La decisione deve seguire l'ambito e la capacità operativa del team, non solo le preferenze tecniche. I microservizi richiedono automazione della distribuzione, osservabilità distribuita, controllo delle versioni dei contratti, tolleranza agli errori di rete, messaggistica, rielaborazione e governance dei dati. Se la squadra non è ancora in grado di operare in sicurezza, la distribuzione troppo presto spesso aumenta il rischio.

Utilizza il monolite modulare quando la priorità è apprendere il dominio, convalidare il prodotto e mantenere l'operazione controllabile. Utilizza i microservizi quando l'indipendenza operativa è già un'esigenza concreta: scala separata, team con reale proprietà, ciclo di rilascio indipendente o requisiti infrastrutturali incompatibili tra le parti del sistema.

Limiti del servizio e del modulo

Un buon limite riduce l’accoppiamento e rende chiaro chi possiede ciascuna regola. Evita di creare servizi per tavolo o per schermo. Servizi e moduli devono rappresentare le capacità aziendali.

  • Catalogare: prodotti, categorie, informazioni commerciali e dati consultati dalle vendite.
  • Saldi: ordini, carrello, prezzi applicati, istantanee e flusso commerciale.
  • Azione: disponibilità, prenotazioni e spostamenti.
  • Sicurezza: utenti, ruoli, autorizzazioni, autenticazione e token.

Un buon limite ha un proprio linguaggio, regole che cambiano insieme, dati sotto una proprietà chiara e contratti espliciti per comunicare con altri contesti. Un limite errato solitamente appare come un modulo “Comune”, un'entità condivisa da più aree, una tabella consultata da tutti o un servizio creato solo perché c'era una nuova schermata.

Quando un modulo necessita di dati da un altro, scegli tra API, evento di integrazione, shadow entity o rimodellamento dei confini. L'accesso diretto al database di un altro modulo è segno di accoppiamento strutturale. Anche quando due moduli vengono eseguiti nello stesso processo e utilizzano lo stesso database fisico, lo schema, i progetti di persistenza e i contratti devono proteggere i confini aziendali.

  • Buon segno limite: il modulo può evolvere le sue entità e migrazioni senza forzare la ricompilazione di un altro modulo a causa dei dettagli interni.
  • Segnale di soglia debole: una semplice modifica a un'entità richiede la revisione di schermate, query e gestori di moduli che non dovrebbero conoscere quell'entità.
  • Segno di dipendenza nascosta: il consumer ha bisogno di “sapere troppo” sulle tabelle, sugli ambiti tecnici o sulle regole interne del producer.

Debito tecnico cosciente

Il debito tecnico non è un’imperfezione qualsiasi. È una decisione di rimandare la qualità conoscendo il costo futuro. Il problema è creare debito senza nome, senza proprietario e senza piano di pagamento.

In un SaaS o in un sistema modulare, il debito tecnico più pericoloso non è solo un metodo lungo. È il debito che limita l’evoluzione: moduli che non possono cambiare separatamente, dati degli inquilini senza isolamento affidabile, secrets all’interno del repository, endpoint sensibili senza limiti, eventi senza rielaborazione o integrazioni senza un contratto chiaro.

  • Scorciatoie del documento adottate durante la generazione e la personalizzazione.
  • Esegui build e test prima di passare alla fase di generazione successiva.
  • Evitare di risolvere i problemi generati da ripetuti hack locali; regolare il disegno o il modello quando il disegno è sbagliato.

Considera ogni decisione architettonica come verificabile. Se scegli di non abilitare la messaggistica, tieni presente che i flussi correnti non si basano sull'elaborazione asincrona. Se scegli la colonna per tenant, assicurati filtri e test. Se scegli l'integrazione sincrona, presupponi il timeout, l'indisponibilità e l'errore contrattuale come parte della progettazione.

Strategie di comunicazione

Scegli la comunicazione in base alla reale esigenza di coerenza, latenza, tracciabilità e accoppiamento. La domanda principale non è “quale tecnologia usare?”, ma “il producer ha bisogno della risposta adesso, se il consumer non dovesse annullare l’operazione e chi possiede i dati?”.

Nei sistemi modulari, l'errore più comune è trasformare la comunicazione in una dipendenza nascosta: un modulo interroga la tabella di un altro, riutilizza un'entità interna, inserisce un repository esterno o crea una chiamata sincrona senza gestire gli errori. All'inizio ciò sembra produttivo, ma rende ogni modifica successiva più rischiosa perché il confine non è più esplicito.

  • Chiamata sincrona: buono per una consultazione immediata e una dipendenza accettabile. Evitare nelle cascate lunghe.
  • Evento del dominio: reazione interna entro lo stesso limite applicativo.
  • Evento di integrazione: comunicazione tra moduli, servizi o sistemi, normalmente con Outbox.
  • Shadow Entity: Copia locale minima per consultazione e validazione senza dipendere direttamente dal producer.
  • Integrazione HTTP esterna: adatto per servizi di terze parti, con timeout, retry, logs e gestione degli errori.

Se la regola richiede un aggiornamento immediato, utilizzare un flusso sincrono o rivedi il limite. Se accetti la consistenza eventuale, preferisci gli eventi per ridurre l'accoppiamento. Quando il consumer ha bisogno solo di leggere un piccolo sottoinsieme di dati da un altro contesto, un'shadow entity può essere più sicura che esporre l'intera entità originale.

DomandaDirezione consigliata
La risposta è necessaria per completare il caso d'uso?Integrazione sincrona esplicita, con timeout, errore prevedibile e contratto chiaro.
Il producer dovrebbe confermare la transazione anche se il consumer è assente?Evento di integrazione con Outbox ed elaborazione asincrona.
Il consumer ha bisogno solo di validare o consultare dati minimi?Shadow Entity alimentata da eventi o integrazioni, con proprietà locale del modello di lettura.
Il cambiamento avviene all’interno dello stesso contesto aggregato o delimitato?Evento del dominio o logica locale del dominio, senza pubblicare contratti esterni non necessari.
Si è verificato un errore non gestito. Ricarica 🗙