Luca Morelli
Analista programmatore
Team lead

Sviluppo software: definire l'architettura o lavorare dentro quella esistente.

Ho definito l'architettura in progetti di cui avevo la responsabilità tecnica. I casi oggi pubblicati raccontano però soprattutto incarichi di consulenza, in cui sono entrato in team e prodotti già avviati. Il mio margine di scelta cambia molto tra queste due situazioni; anche il modo in cui applico metodi e pattern deve cambiare.

Quando progettoScelgo struttura e tecnologie in base al problema, ai vincoli e a chi manterrà il sistema.
Quando entro in un prodottoCapisco le scelte esistenti, seguo le convenzioni del team e contribuisco nel perimetro assegnato.

Due contesti di lavoro

01

Quando definisco l'architettura

Negli incarichi in cui mi è affidata la progettazione, definisco le scelte architetturali e ne assumo la responsabilità.

Parto dalle esigenze e dai vincoli, poi definisco i confini dei moduli, lo stack e il modo in cui le parti comunicano. Considero fin dall'inizio chi dovrà sviluppare, verificare e mantenere il sistema. Introduco un pattern quando risolve un problema concreto o rende più semplice l'evoluzione dell'applicazione.

Le scelte che valuto

  • Responsabilità e confini dei moduli
  • Regole di dominio e casi d'uso
  • Persistenza e integrazioni esterne
  • Testabilità, rilasci e manutenzione
02

Quando entro in un prodotto esistente

Nei progetti di consulenza l'architettura e le linee guida sono spesso già definite.

Il mio primo compito è capire il sistema: convenzioni, dipendenze, flussi tra servizi e ragioni delle scelte fatte. Seguo analisi, sviluppo e verifica dei task che mi vengono affidati, coordinandomi con il team. Se individuo un problema strutturale, propongo un intervento compatibile con il progetto e con il ruolo di chi ne governa l'architettura.

Questa è la situazione rappresentata dalla maggior parte dei casi pubblicati sul sito. La pagina sulle modalità di lavoro descrive più in dettaglio il mio perimetro nei diversi ruoli.

Il mio contributo

  • Analisi del task nel contesto del sistema
  • Sviluppo coerente con gli standard del team
  • Test e controllo delle regressioni
  • Proposte tecniche quando c'è spazio per migliorarne una parte

Backend e frontend

Lavoro nello sviluppo software da oltre venticinque anni. Il mio riferimento principale è l'ecosistema Microsoft, affiancato dalle tecnologie web richieste dai progetti.

Sul backend lavoro soprattutto con .NET e ASP.NET Core: API, regole applicative, dati e integrazioni. Ho seguito l'evoluzione dalla piattaforma .NET Framework alle versioni attuali, lavorando sia per piccole e medie imprese sia in contesti enterprise.

Sul frontend ho una lunga esperienza con Angular: prima AngularJS, poi Angular fin dalle sue prime versioni. Ho utilizzato anche React in progetti di grandi dimensioni. La scelta dello strumento dipende dal prodotto e dal team; in entrambi i casi mi interessa costruire interfacce che restino gestibili quando crescono funzionalità e persone coinvolte.

Organizzare il codice quando ho margine di scelta

Non parto dal nome di un'architettura: parto dalle regole che il codice deve esprimere e dalle modifiche che dovrà sostenere.

Nei moduli con logica di business significativa, separo il dominio, i casi d'uso e i dettagli tecnici. Il dominio contiene concetti e regole dell'attività del cliente; il livello applicativo coordina le operazioni; database, messaggistica e API esterne sono dettagli di integrazione. Applico così il principio centrale della Clean Architecture: le regole del sistema non dipendono direttamente dalla tecnologia usata per conservarle o esporle.

Spesso organizzo il lavoro per funzionalità con un approccio Vertical Slice. Endpoint, richiesta, handler e validazione di un caso d'uso restano vicini, così chi interviene su una feature può seguirne il percorso senza cercare i file in cartelle lontane. Clean Architecture e Vertical Slice rispondono a domande diverse: la prima riguarda le dipendenze, la seconda il modo di raggruppare il codice.

Uso dependency injection per rendere esplicite le dipendenze e separo comandi, query e relativi handler quando questo chiarisce i casi d'uso. Sono strumenti da dosare: in una funzione semplice, aggiungere livelli e astrazioni può rendere il codice più difficile da seguire.

Web / APIEndpoint
Controller
ApplicationCasi d'uso · comandi · query
Interfacce delle integrazioni
DomainEntità · value object
Regole di business
InfrastructureEF Core · SQL · API esterne
Messaggistica
ApplicationInterfacce da implementare
Clean Architecture, in forma semplificata: le dipendenze del codice puntano verso le regole applicative e di dominio; l'infrastruttura implementa le interfacce richieste dall'applicazione.
FeaturesIl codice è raggruppato per funzionalità
Customers / CreateEndpoint · Command · Handler · Validator
Customers / GetEndpoint · Query · Handler
Orders / CreateEndpoint · Command · Handler · Validator
Vertical Slice: i file necessari a una funzionalità sono vicini, invece di essere dispersi in cartelle separate per tipo tecnico.

Microservizi e comunicazione a eventi

Separare i moduli di un'applicazione e distribuire servizi indipendenti sono scelte diverse.

Valuto i microservizi quando esistono confini funzionali chiari e un vantaggio concreto nel far evolvere o rilasciare le parti separatamente. Questa autonomia ha un costo: comunicazioni di rete, gestione degli errori, monitoraggio e coordinamento dei rilasci. In altri contesti può essere più adatta un'applicazione unica, ma organizzata in moduli ben distinti.

Ho lavorato in progetti con comunicazioni asincrone basate su Azure Service Bus o RabbitMQ. Un componente può pubblicare un evento e altri reagire senza una chiamata diretta dal produttore a ogni destinatario. Il disaccoppiamento richiede però attenzione a ritardi, messaggi duplicati, errori e tracciabilità. Nei prodotti esistenti opero entro l'architettura prevista, contribuendo alle integrazioni e alla diagnosi dei flussi; quando sono responsabile della progettazione, valuto se questa modalità sia giustificata dal problema.

FrontendInterfaccia utente
API GatewayIngresso alle API
Customer ServiceResponsabilità: clientiCustomer DB
Order ServiceResponsabilità: ordiniOrder DB
Order ServicePubblica OrderCreated
Bus di messaggiConsegna asincrona
Billing ServiceReagisce all'evento
Esempio illustrativo: servizi con responsabilità e dati separati; alcuni flussi passano dalle API, altri da messaggi asincroni. La divisione richiede confini funzionali e gestione operativa adeguati.
Order ServicePubblica OrderCreated
Message brokerAzure Service Bus o RabbitMQ
BillingPrepara la fatturazione
WarehouseAggiorna il magazzino
NotificationInvia una comunicazione
Comunicazione a eventi, esempio illustrativo: il produttore pubblica una volta e i consumatori interessati reagiscono in modo indipendente. Ogni consumatore deve gestire anche ritardi, duplicati ed errori.

Dominio e test

Le regole importanti devono essere comprensibili a chi conosce il prodotto e verificabili nel tempo.

Uso le idee del Domain-Driven Design nella misura richiesta dal problema: linguaggio condiviso con gli esperti del prodotto, confini tra ambiti funzionali e regole di business collocate dove possano essere comprese e testate. Non ogni applicazione richiede tutti i pattern DDD; nei progetti in team, parto da quelli già adottati e dalle decisioni di chi guida l'architettura.

I test automatici servono a verificare il comportamento e a proteggere le modifiche successive. Uso il TDD quando scrivere prima il test aiuta a precisare una regola o un caso limite; in altre situazioni aggiungo test durante lo sviluppo o riproduco un bug con un test prima di correggerlo. È utile anche verificare un'API quando il client non è ancora pronto. Nei progetti in cui i test sono una condizione per approvare la pull request, seguo i requisiti previsti dal team. La percentuale di copertura resta distinta dalla qualità delle verifiche.

Complessità proporzionata al problema

L'architettura deve aiutare il progetto a crescere, non diventare un fine in sé.

Un sistema che scambia eventi non richiede necessariamente event sourcing. In quest'ultimo caso gli eventi costituiscono la storia persistente da cui si ricostruisce lo stato del sistema: può essere utile quando è essenziale conservare l'evoluzione di un processo, ma aggiunge lavoro su versionamento, proiezioni e gestione nel tempo. Lo valuterei solo davanti a un'esigenza concreta.

Applico lo stesso criterio a livelli, pattern e servizi distribuiti: parto da una struttura proporzionata e la faccio evolvere quando emergono regole, confini o carichi che giustificano una soluzione più articolata. Nei progetti che guido, ne assumo le scelte; quando lavoro in un prodotto altrui, contribuisco a migliorarlo rispettando responsabilità e vincoli del team.

Serve progettare un'applicazione o far evolvere un prodotto esistente?

Curriculum dettagliato e referenze su richiesta.