Parliamone
// tecnologie.microservices-architecture

Microservices Architecture

Strategie di decomposizione, pattern di comunicazione e gestione della consistenza nei sistemi distribuiti a servizi indipendenti.

Software ArchitectureCloud & DevOps

Executive summary

Quando un sistema software cresce oltre una certa soglia di complessità, la sua struttura interna inizia a ostacolare la capacità dei team di rilasciare modifiche in modo rapido e sicuro. L'architettura a servizi indipendenti affronta questo problema suddividendo l'applicazione in componenti autonomi, ciascuno responsabile di una porzione ben definita delle funzionalità di business, che comunicano tra loro attraverso la rete. Questo articolo analizza i principi che guidano la suddivisione del sistema, i meccanismi attraverso cui i componenti si trovano e si scambiano informazioni, e le tecniche per mantenere i dati coerenti quando sono distribuiti su più componenti. Dall'analisi emerge che il vantaggio principale, la possibilità di sviluppare, rilasciare e scalare ogni componente in modo indipendente, comporta costi significativi in termini di complessità operativa, e che le scelte architetturali iniziali sulla granularità dei componenti e sulla gestione dei dati condizionano in modo determinante l'evoluzione del sistema nel lungo periodo.


Background

L'architettura a microservizi si è consolidata come paradigma dominante per la costruzione di sistemi software distribuiti su larga scala a partire dalla metà degli anni 2010, quando Lewis e Fowler [1] ne hanno formalizzato le caratteristiche distintive: servizi di dimensione ridotta, organizzati attorno a capability di business, distribuibili in modo indipendente e comunicanti attraverso meccanismi leggeri. La definizione originale identificava nove proprietà ricorrenti, tra cui la decentralizzazione della governance e della gestione dei dati, il design for failure e l'automazione dell'infrastruttura.

Le radici intellettuali dell'approccio affondano tuttavia in decenni di ricerca precedente. Il principio di information hiding formulato da Parnas nel 1972, la programmazione orientata ai servizi e l'architettura SOA degli anni 2000 hanno tutti contribuito a definire il vocabolario concettuale entro cui i microservizi si collocano. Dragoni et al. [2] ricostruiscono questa genealogia, evidenziando come i microservizi rappresentino un'evoluzione, non una rottura, rispetto ai principi di modularità e separazione delle responsabilità che informano l'ingegneria del software fin dalle origini.

Un contributo fondamentale alla disciplina è il framework di pattern catalogato da Richardson [3], che organizza 44 pattern ricorrenti nelle architetture a microservizi in categorie che coprono decomposizione, comunicazione, gestione dei dati, discovery e osservabilità. Tale catalogo costituisce oggi il riferimento de facto per la progettazione e la valutazione di sistemi distribuiti a servizi indipendenti. Parallelamente, l'opera di Newman [4] ha fornito una guida pratica alla decomposizione e alla migrazione da architetture monolitiche, introducendo concetti quali lo strangler fig pattern e la decomposizione incrementale guidata dal dominio.

Il contesto tecnologico in cui i microservizi operano è stato profondamente trasformato dall'adozione di container orchestrati (Kubernetes), service mesh (Istio, Linkerd) e piattaforme di osservabilità unificate (OpenTelemetry). L'ecosistema Cloud Native Computing Foundation (CNCF) ha standardizzato molte delle primitive infrastrutturali, dal service discovery al distributed tracing, che rendono praticabile l'operazione di centinaia di servizi indipendenti in produzione [5]. Questa convergenza tra principi architetturali e infrastruttura abilitante definisce il perimetro entro cui si muove l'analisi che segue.


Strategie di decomposizione

La decomposizione di un sistema in microservizi è il problema architetturale fondamentale: definire confini errati produce servizi troppo accoppiati che richiedono modifiche coordinate, vanificando il beneficio principale dell'indipendenza di rilascio. La letteratura identifica due strategie primarie, entrambe radicate nel Domain-Driven Design (DDD) di Evans [6]: la decomposizione per business capability e la decomposizione per bounded context.

Decomposizione per business capability

La decomposizione per business capability allinea ogni servizio a una capacità di business dell'organizzazione, ad esempio "gestione ordini", "fatturazione" o "logistica di magazzino". Newman [4] osserva che le capability di business sono relativamente stabili nel tempo rispetto ai processi o alle tecnologie, il che conferisce ai confini dei servizi una maggiore resilienza ai cambiamenti. L'indagine empirica di Bogner et al. [7] conferma che la maggioranza dei professionisti (52,8% del campione intervistato) adotta questa strategia come criterio primario di scomposizione, in quanto produce servizi la cui responsabilità è immediatamente comprensibile in termini organizzativi.

Il rischio principale di questo approccio è la granularità prematura. Lewis e Fowler [1] avvertono che i team con scarsa conoscenza del dominio tendono a tracciare confini troppo fini, generando un'esplosione di servizi con forte accoppiamento implicito. La raccomandazione consolidata è iniziare con servizi più ampi (corrispondenti a bounded context di alto livello) e raffinare i confini solo quando la comprensione del dominio lo giustifica, un principio che Newman sintetizza nella formula "start coarse, refine later" [4].

Decomposizione per bounded context e aggregate

Il DDD fornisce strumenti più fini per la definizione dei confini. Un bounded context delimita un modello di dominio coerente: all'interno del contesto, i termini hanno un significato univoco e le regole di business sono consistenti [6]. Evans chiarisce che bounded context e microservizi non sono la stessa cosa, un bounded context può contenere più servizi o, nelle fasi iniziali, essere implementato come un singolo servizio, ma la corrispondenza uno-a-uno rappresenta il caso ideale in quanto minimizza l'accoppiamento e massimizza la coesione [6].

A un livello di granularità inferiore, gli aggregate definiscono le unità di consistenza transazionale all'interno di un bounded context. Un aggregate è un cluster di entità di dominio con un'entità radice che funge da punto di accesso e garante dell'invariante. Richardson [3] raccomanda di mantenere la ownership di ogni aggregate all'interno di un singolo microservizio, poiché le operazioni che attraversano i confini di un aggregate richiedono necessariamente meccanismi di coordinazione distribuita (saga, eventual consistency) la cui complessità cresce in modo non lineare con il numero di servizi coinvolti.

Approcci automatizzati e semi-automatizzati

La ricerca recente ha esplorato l'uso di tecniche di analisi statica, dinamica e di machine learning per assistere la decomposizione. Fritzsch et al. [8] catalogano approcci che utilizzano clustering su grafi di dipendenza, analisi del codice sorgente con NLP per identificare concetti di dominio, e analisi dei log di esecuzione per rilevare pattern di comunicazione impliciti. Uno studio sistematico del 2024 riporta che il 53,5% delle ricerche sull'uso dell'intelligenza artificiale nella progettazione di microservizi impiega tecniche di machine learning, principalmente algoritmi di clustering, per definire i confini dei servizi, mentre il 32,6% utilizza l'elaborazione del linguaggio naturale per analizzare i requisiti testuali [9]. Questi approcci restano tuttavia complementari al giudizio architetturale: la decomposizione è una decisione che incorpora vincoli organizzativi, di deployment e di performance che nessun algoritmo cattura interamente.


Comunicazione inter-servizio

Una volta definiti i confini dei servizi, la comunicazione tra essi diventa il tessuto connettivo dell'intero sistema. Le scelte relative al protocollo, al pattern di interazione e al livello di accoppiamento temporale determinano le caratteristiche di latenza, affidabilità e manutenibilità dell'architettura complessiva.

Comunicazione sincrona: REST e gRPC

Il modello sincrono request-response è il più immediato: il servizio chiamante invia una richiesta e attende la risposta prima di proseguire. REST su HTTP/1.1 rappresenta l'approccio storicamente più diffuso, grazie alla semplicità e all'ubiquità del protocollo HTTP. Tuttavia, la serializzazione testuale (JSON) e il modello di connessione di HTTP/1.1 introducono overhead significativi in scenari ad alto throughput.

gRPC, il framework RPC sviluppato da Google basato su HTTP/2 e Protocol Buffers, affronta queste limitazioni con serializzazione binaria, multiplexing delle connessioni, streaming bidirezionale e generazione automatica di client e server da definizioni di interfaccia (file .proto). Studi comparativi dimostrano che gRPC offre prestazioni superiori sotto carichi bassi e medi grazie al formato binario e al trasporto non bloccante, con riduzioni di latenza che possono raggiungere il 30-50% rispetto a REST/JSON in scenari di comunicazione intra-cluster [10]. La limitazione principale di gRPC risiede nella minore interoperabilità con client esterni (browser, applicazioni mobile) rispetto a REST, il che suggerisce un pattern consolidato: gRPC per la comunicazione est-ovest tra servizi interni, REST o GraphQL per la comunicazione nord-sud con i client esterni.

Comunicazione asincrona: message broker e event streaming

Il modello asincrono disaccoppia temporalmente produttore e consumatore: il servizio emittente pubblica un messaggio o un evento su un broker intermedio, e il servizio destinatario lo consuma quando è pronto. Questo disaccoppiamento elimina la dipendenza dalla disponibilità simultanea dei servizi e assorbe i picchi di carico attraverso il buffering.

Apache Kafka [11] ha ridefinito lo standard per l'event streaming distribuito, offrendo un log distribuito persistente, partizionato e replicato che garantisce ordinamento all'interno della partizione, at-least-once delivery e throughput nell'ordine di milioni di messaggi al secondo. La semantica di Kafka, in cui gli eventi sono fatti immutabili che descrivono ciò che è accaduto, non comandi che richiedono un'azione, si presta naturalmente a pattern di event sourcing e CQRS, discussi nella sezione sulla gestione dei dati.

La scelta tra comunicazione sincrona e asincrona non è binaria. Le architetture in produzione utilizzano tipicamente entrambi i modelli: chiamate sincrone per query che richiedono risposte immediate (ad esempio la verifica di disponibilità di un prodotto durante il checkout) e comunicazione asincrona per operazioni che tollerano latenza (ad esempio l'aggiornamento delle proiezioni di analytics dopo un ordine completato). Richardson [3] formalizza questa distinzione attraverso i pattern di API Composition (sincrono) e Domain Event (asincrono), raccomandando di valutare per ogni interazione il requisito di consistenza temporale e la tolleranza al fallimento.

Service mesh e comunicazione gestita dall'infrastruttura

Un service mesh astrae la logica di comunicazione inter-servizio, retry, circuit breaking, mutual TLS, load balancing, distributed tracing, dal codice applicativo a un layer infrastrutturale. Istio, che ha raggiunto lo status di graduated project all'interno della CNCF nel 2025 [5], implementa questo pattern attraverso proxy sidecar Envoy iniettati accanto a ogni Pod Kubernetes, con un control plane centralizzato (Istiod) che gestisce service discovery, configurazione e certificati.

L'evoluzione recente verso l'ambient mode di Istio, in beta dal 2024, elimina i sidecar in favore di un layer di rete a livello di nodo, riducendo l'overhead di risorse e la complessità operativa. La maturazione dei service mesh rappresenta un punto di inflessione architetturale: le capability di comunicazione che nel 2018 richiedevano librerie applicative (Hystrix, Resilience4j) o codice custom sono oggi delegate a un'infrastruttura standardizzata, consentendo ai team di concentrarsi sulla logica di dominio.


Service discovery e routing

In un sistema con decine o centinaia di servizi distribuiti su un'infrastruttura elastica, l'indirizzo di rete di ogni istanza è per definizione effimero. Il service discovery è il meccanismo attraverso cui un servizio individua le istanze disponibili di un altro servizio a cui deve inviare una richiesta.

Pattern fondamentali

Richardson [3] cataloga due pattern principali. Nel client-side discovery, il client interroga un service registry (ad esempio Consul, Eureka) per ottenere l'elenco delle istanze disponibili e applica una strategia di load balancing locale. Nel server-side discovery, il client invia la richiesta a un router intermedio (ad esempio un load balancer o un API gateway), che interroga il registry e inoltra la richiesta all'istanza selezionata. Il server-side discovery semplifica il codice del client ma introduce un componente infrastrutturale aggiuntivo che diventa single point of failure se non adeguatamente replicato.

Service discovery in Kubernetes e service mesh

L'adozione di Kubernetes ha di fatto standardizzato il service discovery per le architetture cloud-native. Kubernetes espone ogni Service come un nome DNS stabile che si risolve nell'insieme di Pod corrispondenti, con load balancing integrato via kube-proxy o, nelle configurazioni più recenti, via eBPF (Cilium). Questo meccanismo copre la maggior parte dei casi d'uso di discovery intra-cluster senza richiedere registri esterni [5].

Quando il service mesh è presente, il discovery si arricchisce di capability aggiuntive. Istio integra automaticamente il discovery di Kubernetes e aggiunge routing basato su regole (traffic splitting, canary deployment, fault injection), mutual TLS trasparente e osservabilità a livello di richiesta. Il supporto recente per l'ambient multicluster estende queste funzionalità al routing cross-cluster, consentendo topologie multi-regione senza modifiche applicative [5].

Un aspetto spesso sottovalutato del service discovery è l'integrazione con i meccanismi di health checking e failure detection. In Kubernetes, le probe di liveness e readiness determinano se un Pod è incluso nell'insieme degli endpoint risolvibili dal Service: un Pod che non supera la readiness probe viene rimosso dal pool di discovery, impedendo che il traffico venga instradato verso istanze non operative. Questo meccanismo, combinato con il circuit breaking fornito dal service mesh, implementa il pattern design for failure raccomandato da Lewis e Fowler [1] senza richiedere logica esplicita nel codice applicativo.

L'aspetto critico del service discovery non è dunque l'implementazione tecnica, i meccanismi sono maturi e standardizzati, ma il modello organizzativo: ogni servizio deve pubblicare un contratto di interfaccia versionato, e le dipendenze tra servizi devono essere esplicite e gestite. La proliferazione di dipendenze implicite è una delle cause principali di failure a cascata nei sistemi distribuiti, e strumenti come i service dependency graph generati dall'osservabilità del mesh rappresentano una prima linea di difesa contro questa deriva.


Data ownership e gestione dello stato

Il principio di database-per-service, ogni microservizio possiede il proprio datastore e ne è l'unico punto di accesso, è tra i più consequenziali dell'intero paradigma. Esso garantisce l'incapsulamento dello stato e l'indipendenza di deployment, ma rende impossibili le join relazionali tra servizi e richiede pattern dedicati per query aggregate e consistenza transazionale [3].

Database-per-service e le sue implicazioni

Il pattern database-per-service è la conseguenza diretta del principio di information hiding applicato ai dati. Se il servizio Ordini e il servizio Clienti condividono un database relazionale, qualsiasi modifica allo schema diventa un punto di coordinazione che vincola i rilasci di entrambi i servizi. La separazione dei datastore elimina questo accoppiamento, ma introduce il problema fondamentale della consistenza distribuita: i dati che in un monolite sarebbero stati aggiornati in un'unica transazione ACID risiedono ora in datastore distinti, potenzialmente basati su tecnologie diverse (polyglot persistence).

Kleppmann [12] analizza in profondità le implicazioni di questa scelta, osservando che la rinuncia alla consistenza forte in favore della consistenza eventuale non è un compromesso banale: richiede che il codice applicativo sia progettato per tollerare stati intermedi inconsistenti e che i meccanismi di riconciliazione siano espliciti e verificabili. Le anomalie di lettura (stale reads, phantom reads) che in un database centralizzato sono gestite dall'isolation level del DBMS diventano responsabilità del progettista dell'applicazione.

CQRS e event sourcing

Il pattern Command Query Responsibility Segregation (CQRS) separa il modello di scrittura dal modello di lettura: i comandi che modificano lo stato transitano attraverso il command model, mentre le query utilizzano proiezioni ottimizzate mantenute in sync attraverso eventi [3]. Questa separazione consente di scalare letture e scritture in modo indipendente e di ottimizzare lo schema di ciascun modello per il proprio caso d'uso.

L'event sourcing porta questo principio al limite logico: lo stato corrente del sistema non è memorizzato direttamente, ma ricostruito dalla sequenza ordinata di eventi immutabili che lo hanno prodotto. Ogni modifica è un fatto, "Ordine creato", "Pagamento ricevuto", "Spedizione autorizzata", registrato in un event store append-only. Le proiezioni materializzate vengono derivate dal replay degli eventi e possono essere ricostruite da zero in qualsiasi momento. Kleppmann [12] osserva che event sourcing e CQRS, originati dalla comunità DDD, convergono con i principi dello stream processing emersi indipendentemente in aziende come LinkedIn e Twitter, dove il change data capture (CDC) e i log distribuiti (Kafka) forniscono le primitive infrastrutturali necessarie.

Il costo di queste architetture è la complessità: l'event store richiede una strategia di compaction o snapshotting per evitare che il replay diventi proibitivamente lento, le proiezioni possono divergere dallo stato corrente durante il periodo di propagazione degli eventi, e il debugging richiede strumenti specifici per navigare la storia degli eventi.


Transazioni distribuite e consistenza

L'impossibilità di utilizzare transazioni ACID attraverso i confini dei servizi è il vincolo più profondo dell'architettura a microservizi. Helland [13] ha argomentato nel 2007, ben prima della diffusione dei microservizi, che i sistemi su larga scala non possono dipendere da transazioni distribuite: il protocollo two-phase commit (2PC) richiede che tutti i partecipanti siano disponibili simultaneamente e introduce un coordinatore che diventa un singolo punto di fallimento, con un costo di latenza che cresce linearmente con il numero di partecipanti. Il teorema CAP, formalizzato da Brewer nel 2000 e dimostrato da Gilbert e Lynch nel 2002 [14], stabilisce il vincolo teorico: in presenza di partizioni di rete, inevitabili in un sistema distribuito, un sistema deve scegliere tra consistenza forte e disponibilità.

Il pattern Saga

Il pattern Saga, proposto originariamente da Garcia-Molina e Salem nel 1987 [15] per gestire transazioni di lunga durata nei database, è stato adottato dalla comunità microservizi come alternativa al 2PC. Una saga è una sequenza di transazioni locali, ciascuna eseguita all'interno di un singolo servizio: se una transazione fallisce, vengono eseguite transazioni compensative per annullare gli effetti delle transazioni precedenti.

Richardson [3] identifica due strategie di coordinazione, illustrate nel diagramma seguente.

Figura 1, Coreografia vs. orchestrazione nel pattern Saga.

graph LR
    subgraph "Coreografia"
        A1[Servizio Ordini] -- "Ordine creato" --> B1[Event Bus]
        B1 -- "Ordine creato" --> C1[Servizio Pagamenti]
        C1 -- "Pagamento confermato" --> B1
        B1 -- "Pagamento confermato" --> D1[Servizio Inventario]
        D1 -- "Inventario riservato" --> B1
    end

    subgraph "Orchestrazione"
        A2[Orchestratore Saga] --> C2[Servizio Pagamenti]
        A2 --> D2[Servizio Inventario]
        A2 --> E2[Servizio Spedizioni]
        C2 -. "risposta" .-> A2
        D2 -. "risposta" .-> A2
        E2 -. "risposta" .-> A2
    end

Nella coreografia, ogni servizio reagisce agli eventi emessi dagli altri servizi e decide autonomamente l'azione successiva: il servizio Ordini emette l'evento "Ordine creato", il servizio Pagamenti lo intercetta e tenta l'addebito, emettendo a sua volta "Pagamento confermato" o "Pagamento rifiutato". Non esiste un coordinatore centrale, il che favorisce il disaccoppiamento ma rende difficile comprendere il flusso complessivo quando il numero di servizi cresce. Nell'orchestrazione, un servizio dedicato (l'orchestratore) gestisce esplicitamente la sequenza di chiamate e le compensazioni, offrendo un punto di controllo centralizzato a scapito di un maggiore accoppiamento con l'orchestratore stesso.

Il problema dell'isolamento

La limitazione fondamentale delle saga è l'assenza di isolamento: le transazioni locali di una saga possono leggere e scrivere dati che sono in uno stato intermedio rispetto a un'altra saga concorrente. Garcia-Molina e Salem [15] avevano già identificato questo problema nel paper originale, e la ricerca successiva ha proposto contromisure specifiche. Babaei et al. [16] analizzano le anomalie derivanti dalla mancanza di isolamento, dirty reads, lost updates, fuzzy reads, e propongono un'estensione del pattern basata su quota cache e commit-sync service che serializza gli accessi ai dati condivisi tra saga concorrenti, ristabilendo un livello di isolamento controllato senza ricorrere al 2PC.

In pratica, i progettisti di saga devono applicare contromisure a livello applicativo: semantic locking (marcatura esplicita degli aggregate in stato "in elaborazione"), commutative updates (operazioni che producono lo stesso risultato indipendentemente dall'ordine di esecuzione), e pessimistic view (proiezioni di lettura che espongono solo dati di saga completate). Richardson [3] cataloga queste contromisure e osserva che la scelta tra coreografia e orchestrazione dipende dalla complessità delle compensazioni e dal livello di isolamento richiesto: la coreografia è adeguata per saga semplici (3-4 passaggi), mentre l'orchestrazione diventa preferibile quando la logica di compensazione è complessa o quando è necessario un audit trail esplicito.

Consistenza eventuale come paradigma operativo

L'adozione della consistenza eventuale non è un difetto dell'architettura ma una scelta progettuale deliberata che riflette la realtà fisica dei sistemi distribuiti. Il teorema CAP [14] e l'analisi di Helland [13] convergono sulla stessa conclusione: quando i dati risiedono su nodi separati da una rete, la consistenza forte è ottenibile solo sacrificando la disponibilità durante le partizioni. Per la maggior parte dei casi d'uso, cataloghi prodotti, carrelli, notifiche, analytics, la consistenza eventuale è non solo accettabile ma preferibile, in quanto consente al sistema di rimanere operativo anche durante guasti parziali.

Il costo si manifesta nella progettazione dell'interfaccia utente e dei processi di business, che devono essere consapevoli della possibile inconsistenza temporanea. Un sistema di e-commerce, ad esempio, può mostrare un prodotto come disponibile nel catalogo anche se l'ultimo aggiornamento dell'inventario non è ancora stato propagato: il conflitto viene risolto al momento del checkout, quando la transazione locale del servizio inventario verifica la disponibilità effettiva. Questo pattern, ottimismo in lettura, validazione in scrittura, è un idioma ricorrente nei sistemi distribuiti a microservizi.


Limiti e problemi aperti

L'architettura a microservizi introduce una classe di complessità che non ha equivalenti nelle architetture monolitiche: la complessità operativa distribuita. I problemi aperti più significativi riguardano almeno quattro aree.

Osservabilità e debugging. Il distributed tracing, la capacità di seguire una richiesta attraverso decine di servizi, è una necessità operativa primaria. OpenTelemetry, il secondo progetto più attivo della CNCF dopo Kubernetes, ha standardizzato la raccolta di trace, metriche e log attraverso SDK multilingua e un Collector unificato [5]. Nonostante questa standardizzazione, il debugging di anomalie che emergono dall'interazione di più servizi resta significativamente più complesso rispetto al debugging di un monolite: le cause radice sono spesso emergenti, non localizzabili in un singolo servizio.

Testing e integrazione. Il testing end-to-end di un sistema a microservizi richiede la disponibilità simultanea di tutti i servizi coinvolti in un flusso, con i rispettivi datastore e broker. Le strategie di contract testing (Pact, Spring Cloud Contract) mitigano parzialmente il problema verificando la compatibilità delle interfacce senza richiedere l'esecuzione dell'intero sistema, ma non coprono le anomalie di consistenza distribuita che emergono solo sotto carico reale.

Granularità ottimale. Non esiste una metrica oggettiva per determinare la dimensione ideale di un microservizio. La letteratura riporta sistemi con servizi che gestiscono un singolo aggregate e sistemi con servizi corrispondenti a interi bounded context, entrambi operativi in produzione. La granularità ottimale dipende dalla frequenza di rilascio, dalla struttura dei team (Conway's Law), dai requisiti di scalabilità e dal livello di accoppiamento tollerabile. La decomposizione prematura, suddividere il sistema prima di comprenderne a sufficienza il dominio, è riconosciuta dalla letteratura come l'errore più costoso, in quanto i costi di ri-consolidamento sono significativamente superiori a quelli di decomposizione incrementale [1, 4].

Governance e standardizzazione. La decentralizzazione che i microservizi promuovono genera inevitabilmente divergenza: linguaggi diversi, framework diversi, pattern di osservabilità diversi. Senza una governance architetturale esplicita, inner source, platform team, golden path, il costo di manutenzione cresce in modo superlineare con il numero di servizi. La tensione tra autonomia dei team e coerenza del sistema resta un problema organizzativo tanto quanto tecnico.


Riferimenti

[1] J. Lewis, M. Fowler, "Microservices: A Definition of This New Architectural Term," martinfowler.com, 2014. https://martinfowler.com/articles/microservices.html

[2] N. Dragoni et al., "Microservices: Yesterday, Today, and Tomorrow," in Present and Ulterior Software Engineering, Springer, 2017, pp. 195-216. https://arxiv.org/abs/1606.04036

[3] C. Richardson, Microservices Patterns: With Examples in Java, Manning Publications, 2018. https://microservices.io/patterns/

[4] S. Newman, Building Microservices: Designing Fine-Grained Systems, 2nd ed., O'Reilly Media, 2021.

[5] Cloud Native Computing Foundation, "CNCF Project Landscape," 2025. https://www.cncf.io/projects/

[6] E. Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software, Addison-Wesley, 2003.

[7] J. Bogner, J. Fritzsch, S. Wagner, A. Zimmermann, "Assessing Microservice Architecture Styles: Insights from Industry Practitioners," in Proc. IEEE International Conference on Software Architecture (ICSA), 2024. https://arxiv.org/abs/2408.10434

[8] J. Fritzsch et al., "Microservices-based Software Systems Reengineering: State-of-the-Art and Future Directions," arXiv:2407.13915, 2024. https://arxiv.org/abs/2407.13915

[9] A. H. Rasool et al., "A Survey on Microservices Architecture: Principles, Patterns and Migration Challenges," IEEE Transactions on Services Computing, vol. 16, no. 5, 2023. https://ieeexplore.ieee.org/document/10220070

[10] S. Kumar et al., "Impact of Protocol Selection on Performance and Scalability in Microservices: A Comparison of gRPC, REST, and GraphQL," 2025. https://www.researchgate.net/publication/392507557

[11] Apache Software Foundation, "Apache Kafka Documentation," 2025. https://kafka.apache.org/documentation/

[12] M. Kleppmann, Designing Data-Intensive Applications, O'Reilly Media, 2017.

[13] P. Helland, "Life beyond Distributed Transactions: an Apostate's Opinion," in Proc. CIDR, 2007. https://ics.uci.edu/~cs223/papers/cidr07p15.pdf

[14] S. Gilbert, N. Lynch, "Brewer's Conjecture and the Feasibility of Consistent, Available, Partition-Tolerant Web Services," ACM SIGACT News, vol. 33, no. 2, 2002. https://groups.csail.mit.edu/tds/papers/Gilbert/Brewer2.pdf

[15] H. Garcia-Molina, K. Salem, "Sagas," in Proc. ACM SIGMOD, 1987, pp. 249-259. https://dl.acm.org/doi/10.1145/38713.38742

[16] H. Babaei et al., "Enhancing Saga Pattern for Distributed Transactions within a Microservices Architecture," Applied Sciences, vol. 12, no. 12, 2022. https://www.mdpi.com/2076-3417/12/12/6242

Microservices Architecture

Raccontaci la situazione. Rispondiamo entro 24 ore nei giorni lavorativi.

Tweaks

Light mode
Atmospheric (glass)
Client logos
Terminal hero