← Torna a tutti gli articoli
Sfide

Monolite vs. Microservizi: Quale Architettura Scegliere per il tuo MVP

Di Marc Molas·22 dicembre 2023·10 min di lettura

C'è una conversazione che ho almeno due volte al mese con founder che arrivano chiedendo aiuto con il loro stack. Inizia così: "Vogliamo costruire l'MVP con i microservizi fin dall'inizio per non dover riscrivere dopo."

È un'idea che suona logica. E nella stragrande maggioranza dei casi, è un errore.

L'ossessione per i microservizi viene da un posto comprensibile. Netflix, Spotify, Amazon — le aziende che più ammiriamo in tecnologia evangelizzano questa architettura da anni. Gli articoli sui blog tecnici, i talk alle conferenze e i thread su Twitter ripetono lo stesso messaggio: i monoliti sono legacy, i microservizi sono il futuro.

Quello che quegli articoli non raccontano è che Netflix è partita come monolite. Spotify è partita come monolite. Amazon è partita come monolite. Sono migrati ai microservizi quando avevano centinaia di ingegneri, milioni di utenti e necessità di scalabilità che un monolite non poteva soddisfare. Non nella fase di MVP.

Perché i monoliti vincono per gli MVP

Un monolite è un'applicazione distribuita come un'unità unica. Un repository, un processo, un database. E per un MVP, è esattamente ciò che ti serve per ragioni molto concrete:

Velocità di sviluppo. In un monolite, un ingegnere può implementare una funzionalità end-to-end senza coordinarsi con altri servizi, senza gestire comunicazione tra processi e senza preoccuparsi della consistenza di dati distribuiti. Chiami una funzione, non un endpoint HTTP. La differenza nella velocità di iterazione è brutale.

Semplicità di deploy. Una pipeline di CI/CD. Un server (o un container). Un deploy. Non ti servono Kubernetes, service mesh, service discovery né coordinamento di versioni tra servizi. Quando il tuo team è di 3-5 persone, questa semplicità non è un lusso — è sopravvivenza.

Debugging diretto. Quando qualcosa fallisce in un monolite, hai uno stack trace. Un percorso chiaro dall'errore alla causa. Nei microservizi, un fallimento può propagarsi attraverso tre servizi diversi, con log distribuiti in sistemi differenti, latenze di rete che oscurano la causa radice e fallimenti a cascata che trasformano un bug semplice in un'indagine di ore.

Refactoring senza paura. In un monolite, puoi spostare codice tra moduli, rinominare funzioni e cambiare interfacce interne senza rompere contratti di API tra servizi. Il tuo IDE ti mostra tutti i riferimenti. I tuoi test coprono il flusso completo. Nei microservizi, una modifica allo schema di un evento può rompere tre servizi downstream senza che tu te ne accorga fino a quando arriva in produzione.

Un unico modello dati. Un database, uno schema, un'unica fonte di verità. Non devi decidere quale servizio è il "proprietario" dell'entità utente, non devi sincronizzare dati tra servizi, non ti serve l'eventual consistency quando il tuo prodotto sta ancora definendo quali dati deve archiviare.

Quando i microservizi hanno davvero senso

I microservizi risolvono problemi reali. Ma sono problemi che la maggior parte degli MVP non ha:

Più team che lavorano in parallelo. Se hai 30 ingegneri divisi in 6 team, un monolite diventa un collo di bottiglia. I conflitti di merge sono costanti, i deploy richiedono coordinamento tra team e un errore di un team può bloccare il rilascio di tutti. I microservizi permettono a ogni team di fare deploy in modo indipendente.

Necessità di scaling differenziate. Il tuo servizio di elaborazione immagini richiede GPU e scala con il volume di upload. La tua API di ricerca richiede molta RAM e scala con il numero di query. Il tuo servizio di notifiche è ad uso intensivo di I/O e scala con gli utenti attivi. Se ogni componente ha pattern di scaling radicalmente diversi, distribuirli separatamente permette di ottimizzare le risorse.

Requisiti tecnologici diversi per servizio. Il tuo motore di raccomandazione funziona meglio in Python con librerie ML. La tua API real-time necessita WebSocket in Node.js. Il tuo sistema di fatturazione richiede le garanzie transazionali di Java. I microservizi ti permettono di scegliere la tecnologia ottimale per ogni dominio.

Il tuo MVP ha 30 ingegneri? Ha bisogno di scalare componenti in modo indipendente? Richiede tre linguaggi di programmazione diversi? Probabilmente no. Allora non ti servono i microservizi.

Il sweet spot: il monolite modulare

La scelta migliore per la maggior parte delle startup non è né il monolite classico (tutto accoppiato) né i microservizi (tutto separato). È il monolite modulare.

Un monolite modulare si distribuisce come un'unità, ma internamente è organizzato in moduli con confini chiari. Ogni modulo ha la propria logica di business, i propri modelli dati e un'interfaccia pubblica ben definita. I moduli comunicano attraverso interfacce interne — non chiamate HTTP, ma contratti di codice.

Mantieni tutta la semplicità del monolite (un deploy, un database, debugging diretto), ma con la modularità di cui hai bisogno per scalare. Quando arriverà il momento di estrarre un modulo come servizio indipendente, i confini sono già definiti.

La chiave è la disciplina. Non chiamare direttamente le funzioni interne di un altro modulo. Non condividere modelli dati tra moduli. Usa l'interfaccia pubblica. La tentazione di prendere scorciatoie è costante quando tutto si trova nello stesso repository — e qui è dove un ingegnere senior con esperienza in architettura fa la differenza.

I costi reali dei microservizi prematuri

Se sei ancora tentato dai microservizi per il tuo MVP, considera i costi che raramente compaiono negli articoli che li promuovono:

Complessità DevOps. Ogni servizio ha bisogno della propria pipeline CI/CD, della propria configurazione di deploy, del proprio monitoraggio. Con 5 microservizi, hai 5 pipeline da mantenere. Con un monolite, ne hai una.

Overhead infrastrutturale. Kubernetes, service mesh, logging centralizzato, tracing distribuito, health check per servizio. Niente di questo è gratis — né in denaro né in tempo di ingegneria.

Latenza di rete. Quella che prima era una chiamata a funzione in memoria (nanosecondi) ora è una chiamata HTTP o gRPC (millisecondi). Moltiplica per ogni interazione tra servizi in una request dell'utente.

Consistenza dei dati. Con un database per servizio perdi le transazioni ACID tra servizi. Ti servono saga o eventual consistency. Per un MVP che sta ancora definendo il suo modello dati, questa è complessità prematura.

Debugging distribuito. Un bug che in un monolite si diagnostica in 15 minuti può richiedere mezza giornata nei microservizi. L'errore è nato nel servizio A, si è propagato al B e l'utente lo ha visto nel C. Senza tracing distribuito ben configurato, navighi alla cieca.

Un framework decisionale semplice

Prima di scegliere l'architettura, rispondi a queste domande:

  • Il tuo team ha meno di 8 ingegneri? → Monolite.
  • Stai costruendo un singolo prodotto? → Monolite.
  • Sei in fase pre-seed, seed o Serie A iniziale? → Monolite.
  • La tua priorità è velocità di iterazione e time-to-market? → Monolite.
  • Puoi sostenere il costo di un team DevOps dedicato? → Se no, monolite.

Se hai risposto "monolite" a tutto: costruisci un monolite modulare con confini chiari tra moduli. Quando avrai 20+ ingegneri, più team e necessità di scaling differenziate, sarai in posizione di estrarre servizi in modo incrementale — senza riscrivere da zero.

Come progettare un monolite facile da scomporre dopo

Se accetti che il monolite è la decisione giusta oggi, la domanda successiva è come evitare che diventi una palla di fango impossibile da scomporre quando arriverà il momento. Queste sono le pratiche che funzionano:

  • Organizza il codice per dominio di business, non per layer tecnico. Invece di cartelle /controllers, /models, /services, usa /billing, /users, /notifications. Ogni dominio contiene i propri controller, modelli e servizi.
  • Definisci interfacce esplicite tra moduli. Ogni modulo espone un'API interna (funzioni, classi, interfacce) e nasconde la propria implementazione. Nessun modulo accede direttamente al database di un altro.
  • Un database, ma schema separati. Usa schema o prefissi di tabella per separare i dati di ogni modulo. Questo facilita l'estrazione futura.
  • Evita dipendenze circolari. Se il modulo A dipende da B e B dipende da A, hai un problema di design che sarà molto peggiore quando proverai a separarli.
  • Test per modulo. Ogni modulo ha i propri test unitari e di integrazione. Se puoi testare un modulo in isolamento, puoi estrarlo in isolamento.

Queste pratiche non richiedono più tempo di sviluppo. Richiedono più disciplina di design. E quella disciplina è ciò che distingue un ingegnere senior da uno junior — non il framework che usa, ma come struttura il codice affinché il team che verrà dopo possa lavorarci.

In Conectia, gli ingegneri senior che forniamo alle startup europee hanno costruito e scomposto monoliti in aziende di tutte le dimensioni. Non arrivano con un'opinione dogmatica su monoliti vs. microservizi — arrivano con l'esperienza per valutare il tuo caso concreto e prendere la decisione giusta. Perché l'architettura giusta non è quella di moda. È quella che ti permette di consegnare valore all'utente il più velocemente possibile con il team che hai oggi.


Non sai quale architettura serve al tuo prodotto? Parla con un CTO — valutiamo il tuo caso e ti raccomandiamo l'architettura adatta alla tua fase e al tuo team.

Pronto a costruire il tuo team di ingegneria?

Parla con un partner tecnico e distribuisci sviluppatori validati da CTO in 72 ore.