L'arquitectura monolítica és el punt de partida històric i conceptual de gairebé qualsevol sistema de programari. Consisteix a construir i desplegar una aplicació com una única unitat cohesionada: tot el codi (interfície d'usuari, lògica de negoci i accés a dades) viu en el mateix projecte, es compila conjuntament i s'executa en un sol procés. Malgrat la moda dels microserveis, comprendre el monòlit és imprescindible, perquè continua sent l'opció més sensata per a la majoria de projectes que comencen, i perquè molts dels "antipatrons" que pateixen els sistemes distribuïts neixen de no haver entès bé com organitzar un bon monòlit. En aquesta lliçó distingirem el monòlit clàssic del monòlit modular, veurem quan té sentit cadascun i desmuntarem diversos mites estesos.
Contingut
- Què és un monòlit i per què importa
- Monòlit clàssic (big ball of mud)
- Monòlit modular
- Comparativa: clàssic vs modular vs microserveis
- Quan té sentit un monòlit
- Mites sobre el monòlit
- Errors comuns i consells
- Exercicis
- Conclusió
- Què és un monòlit i per què importa
Un monòlit és una aplicació que s'empaqueta i es desplega com una sola unitat executable. En el món Java sol ser un únic .jar o .war; en altres entorns, un únic contenidor o procés.
Característiques definitòries:
- Un sol artefacte de desplegament. Tota l'aplicació s'allibera alhora.
- Un sol procés en execució. Les crides entre mòduls són crides a mètode en memòria, no peticions de xarxa.
- Una base de codi compartida. Tots els equips treballen sobre el mateix repositori (habitualment).
- Normalment, una sola base de dades. Encara que no és obligatori.
graph TD
Usuario[Usuari / Navegador] --> Mono
subgraph Mono[Procés únic - Monòlit]
UI[Capa de Presentació]
BL[Lògica de Negoci]
DA[Accés a Dades]
UI --> BL --> DA
end
DA --> DB[(Base de Dades)]El diagrama mostra que, encara que internament existeixi una separació lògica en capes, tot s'executa dins del mateix procés i es desplega conjuntament. Aquesta és la diferència essencial amb un sistema distribuït.
- Monòlit clàssic (big ball of mud)
El monòlit clàssic és la forma més comuna i, sovint, la pitjor entesa. No és dolent per ser monòlit, sinó per com sol degenerar: quan no s'imposen fronteres internes clares, el codi acaba en allò que Brian Foote i Joseph Yoder van batejar com a "big ball of mud" (gran bola de fang).
Símptomes d'un monòlit degradat:
- Qualsevol classe pot cridar qualsevol altra; no hi ha mòduls reals.
- La lògica de negoci es barreja amb l'accés a dades i amb la presentació.
- Un canvi petit obliga a tocar desenes de fitxers per tot el projecte.
- El coneixement del sistema viu només al cap d'unes poques persones.
// Antipatró típic del monòlit clàssic: tot barrejat en un controlador
@RestController
public class PedidoController {
@Autowired private DataSource dataSource; // accés a dades directe
@PostMapping("/pedidos")
public String crearPedido(@RequestBody Map<String,Object> body) throws Exception {
// 1) Lògica de presentació (parseig) barrejada
String cliente = (String) body.get("cliente");
double total = (Double) body.get("total");
// 2) Regles de negoci incrustades en el controlador
if (total > 1000) total = total * 0.95; // descompte "màgic"
// 3) SQL cru a la mateixa classe
try (var con = dataSource.getConnection()) {
var ps = con.prepareStatement(
"INSERT INTO pedidos(cliente,total) VALUES(?,?)");
ps.setString(1, cliente);
ps.setDouble(2, total);
ps.executeUpdate();
}
return "OK"; // sense gestió d'errors seriosa
}
}Analitzem per què aquest fragment és problemàtic:
- Barreja de responsabilitats: el
@RestControllerparseja la petició (presentació), aplica un descompte (negoci) i executa SQL (persistència). Tres responsabilitats diferents en un sol mètode. - Regla de negoci enterrada: el descompte del 5% està amagat en el controlador. Si un altre endpoint crea comandes, aquesta regla es duplicarà o s'oblidarà.
- Acoblament a la infraestructura: el SQL cru i el
DataSourcefan que la lògica sigui impossible de provar sense una base de dades real.
Important: ser monòlit no obliga a escriure així. Aquest és el monòlit mal fet. Vegem com evitar-ho.
- Monòlit modular
El monòlit modular conserva tots els avantatges operatius del monòlit (un sol desplegament, sense xarxa entre mòduls) però imposa fronteres internes estrictes entre mòduls de negoci. Cada mòdul exposa una API pública i amaga els seus detalls interns; els altres mòduls només poden comunicar-se a través d'aquesta API.
graph TD
subgraph Monolito Modular
direction LR
Pedidos[Mòdul Comandes\nAPI pública]
Clientes[Mòdul Clients\nAPI pública]
Facturas[Mòdul Factures\nAPI pública]
Pedidos --> Clientes
Facturas --> Pedidos
endLes claus d'un monòlit modular:
- Encapsulació per mòdul: cada mòdul té el seu propi paquet arrel i amaga les seves classes internes.
- Comunicació per contractes: els mòduls es parlen a través d'interfícies, no accedint a classes internes.
- Cohesió per domini: s'agrupa per capacitat de negoci (Comandes, Clients, Facturació), no per capa tècnica.
// Mòdul Comandes: API pública (l'única cosa que veuen els altres mòduls)
package com.fiatc.pedidos.api;
public interface ServicioPedidos {
Long crearPedido(NuevoPedido datos);
}
// Implementació interna (package-private, invisible fora del mòdul)
package com.fiatc.pedidos.internal;
import com.fiatc.pedidos.api.ServicioPedidos;
class ServicioPedidosImpl implements ServicioPedidos {
private final RepositorioPedidos repositorio;
private final PoliticaDescuentos descuentos; // regla aïllada i testejable
ServicioPedidosImpl(RepositorioPedidos r, PoliticaDescuentos d) {
this.repositorio = r;
this.descuentos = d;
}
@Override
public Long crearPedido(NuevoPedido datos) {
double total = descuentos.aplicar(datos.total());
return repositorio.guardar(datos.cliente(), total);
}
}Què ha millorat respecte a l'exemple anterior:
- La classe
ServicioPedidosImplésclasssensepublic(package-private): els altres mòduls no poden instanciar-la ni acoblar-s'hi; només coneixen la interfícieServicioPedidos. - La regla de descompte viu a
PoliticaDescuentos, una classe única i reutilitzable que es pot provar de manera aïllada. - L'accés a dades està darrere de
RepositorioPedidos, per la qual cosa la lògica de negoci no sap res de SQL.
Eines com Spring Modulith o el sistema de mòduls de Java (JPMS) ajuden a verificar automàticament que aquestes fronteres no es trenquin.
- Comparativa: clàssic vs modular vs microserveis
| Criteri | Monòlit clàssic | Monòlit modular | Microserveis |
|---|---|---|---|
| Fronteres internes | Difuses o inexistents | Estrictes (en codi) | Físiques (xarxa) |
| Desplegament | Únic | Únic | Múltiple i independent |
| Comunicació entre mòduls | Crida en memòria | Crida en memòria via API | Xarxa (HTTP/missatgeria) |
| Cost operatiu | Baix | Baix | Alt (orquestració, observabilitat) |
| Escalat | Tot o res | Tot o res | Granular per servei |
| Complexitat inicial | Baixa | Mitjana | Molt alta |
| Risc de "bola de fang" | Alt | Baix | Es trasllada al sistema distribuït |
| Refactor de fronteres | Costós (acoblament) | Barat (és codi) | Molt costós (contractes de xarxa) |
La lliçó clau de la taula: el monòlit modular sol ser el "punt dolç" per a molts equips. Captura disciplina arquitectònica sense pagar el cost operatiu del que és distribuït, i deixa la porta oberta a extreure microserveis més endavant si un mòdul ho necessita.
- Quan té sentit un monòlit
Un monòlit (preferiblement modular) és una bona elecció quan:
- L'equip és petit o mitjà (fins a unes poques desenes de persones).
- El domini encara no està clar: les fronteres dels mòduls canviaran molt, i refactoritzar dins d'un monòlit és barat.
- No hi ha necessitats d'escalat dispars: totes les parts del sistema reben una càrrega similar.
- Es vol maximitzar la velocitat de lliurament inicial i minimitzar la complexitat operativa.
- Les transaccions creuen diversos mòduls: dins d'un procés es resolen amb una transacció ACID local; en el que és distribuït requereixen patrons complexos (sagues).
Regla pràctica molt citada (Martin Fowler, MonolithFirst): comença amb un monòlit i extreu serveis només quan el dolor ho justifiqui.
- Mites sobre el monòlit
| Mite | Realitat |
|---|---|
| "Monòlit = codi espagueti" | El desordre ve de la manca de fronteres, no del desplegament únic. Un monòlit modular pot estar més ben organitzat que un mar de microserveis. |
| "Els monòlits no escalen" | Escalen horitzontalment desplegant diverses instàncies darrere d'un balancejador. El que no escala és l'escalat granular per component. |
| "Cal començar amb microserveis per no migrar després" | Començar distribuït amb un domini immadur sol produir fronteres errònies molt cares de moure. |
| "Un monòlit implica una sola tecnologia/llenguatge" | Cert en el procés, però rarament és un problema real davant dels avantatges. |
| "Desplegar un monòlit sempre és lent" | Amb CI/CD moderns, compilar i desplegar un monòlit ben estructurat és perfectament àgil. |
- Errors Comuns i Consells
- No imposar fronteres des del primer dia. Encara que sigui un monòlit, organitza per domini i amaga els detalls interns. És l'única cosa que evita la bola de fang.
- Organitzar per capa tècnica en lloc de per domini. Paquets
controllers,services,repositoriesglobals fomenten l'acoblament. Prefereixpedidos,clientes,facturasi, dins, les capes. - Confondre "modular" amb "molts fitxers". Modular significa encapsulació real (visibilitat package-private, APIs explícites), no només dividir en paquets.
- Saltar a microserveis per moda. Si el teu monòlit fa mal, primer pregunta't si el problema és de modularitat, no de desplegament.
- Consell: utilitza eines com Spring Modulith o ArchUnit per verificar automàticament que ningú no creua les fronteres dels mòduls.
- Exercicis
Exercici 1. Tens un monòlit amb paquets globals controllers, services i repositories. Proposa una reorganització per domini per a una aplicació d'assegurances amb les capacitats: Pòlisses, Sinistres i Clients. Descriu l'estructura de paquets.
Exercici 2. Donat el PedidoController de l'apartat 2, identifica les tres responsabilitats barrejades i proposa a quina classe/col·laborador hauria d'anar cadascuna.
Exercici 3. Raona si els següents escenaris justifiquen microserveis o un monòlit modular: (a) Startup de 4 desenvolupadors amb domini incert. (b) Un component concret rep 100 vegades més càrrega que la resta i ha d'escalar per separat.
Solucions
Solució 1. Estructura orientada a domini:
com.fiatc.seguros
├── polizas
│ ├── api (interfícies públiques: ServicioPolizas)
│ └── internal (impl, repositori, entitats - package-private)
├── siniestros
│ ├── api
│ └── internal
└── clientes
├── api
└── internalCada domini conté les seves pròpies capes; els dominis es comuniquen només a través dels paquets api.
Solució 2.
- Parseig de la petició HTTP → responsabilitat de presentació; ha de quedar-se en el controlador, però delegant immediatament.
- Descompte del 5% → regla de negoci; ha d'anar a una classe com
PoliticaDescuentosdins del mòdul Comandes. - INSERT a la BD → persistència; ha d'anar a un
RepositorioPedidos.
El controlador s'hauria de limitar a rebre el DTO i cridar ServicioPedidos.crearPedido(...).
Solució 3. (a) Monòlit modular. Domini incert i equip petit: les fronteres canviaran i el refactor ha de ser barat. (b) Candidat a extreure un microservei per a aquest component concret, si l'escalat granular justifica el cost operatiu. Tot i així, convé haver-lo aïllat primer com a mòdul dins del monòlit.
- Conclusió
El monòlit no és el dolent de l'arquitectura moderna; el dolent és la manca de fronteres internes. Un monòlit modular ofereix simplicitat operativa, transaccions locals i refactor barat, i és la millor opció de partida per a la immensa majoria de projectes. Hem vist la diferència amb el monòlit clàssic, quan triar-lo i els mites que convé desmuntar. En la següent lliçó aprofundirem en com estructurar internament qualsevol aplicació —monolítica o no— mitjançant l'Arquitectura en Capes (N-Tier), on estudiarem les capes de presentació, negoci i persistència, i antipatrons clàssics com el de la "capa embornal".
Curs d'Arquitectura d'Aplicacions
Mòdul 1: Fonaments de l'Arquitectura d'Aplicacions
- Què és l'Arquitectura d'Aplicacions?
- El Rol de l'Arquitecte de Programari
- Atributs de Qualitat i Requisits No Funcionals
- Decisions Arquitectòniques i Compromisos (Trade-offs)
- Documentació d'Arquitectura: Vistes i el Model C4
Mòdul 2: Principis i Tàctiques de Disseny
- Acoblament, Cohesió i Separació de Responsabilitats
- Principis SOLID Aplicats a l'Arquitectura
- DRY, KISS, YAGNI i Altres Principis de Disseny
- Tàctiques Arquitectòniques per als Atributs de Qualitat
- Gestió del Deute Tècnic
Mòdul 3: Estils i Patrons Arquitectònics
- Arquitectura Monolítica
- Arquitectura en Capes (N-Tier)
- Arquitectura Client-Servidor
- Arquitectura Hexagonal (Ports i Adaptadors)
- Arquitectura Neta i Ceba (Clean & Onion)
Mòdul 4: Arquitectures Distribuïdes i Microserveis
- Introducció als Sistemes Distribuïts
- Arquitectura de Microserveis
- Descomposició de Serveis i Bounded Contexts
- API Gateway, Service Discovery i Comunicació entre Serveis
- Patrons de Resiliència: Circuit Breaker, Retry i Bulkhead
- El Teorema CAP i la Consistència de Dades
Mòdul 5: Arquitectures Dirigides per Esdeveniments i Missatgeria
- Fonaments de l'Arquitectura Orientada a Esdeveniments
- Missatgeria Asíncrona: Cues i Brokers
- Patrons d'Esdeveniments: Event Sourcing i CQRS
- Gestió de Transaccions Distribuïdes: Patró Saga
- Streaming de Dades en Temps Real
Mòdul 6: Disseny Dirigit pel Domini (DDD)
- Conceptes Fonamentals del DDD
- Disseny Estratègic: Bounded Contexts i Llenguatge Ubic
- Disseny Tàctic: Entitats, Agregats i Repositoris
- Mapatge de Contextos (Context Mapping)
Mòdul 7: Dades i Persistència
- Estratègies de Persistència: SQL vs NoSQL
- Patrons d'Accés a Dades: Repository, Unit of Work i DAO
- Base de Dades per Servei i Gestió de Dades Distribuïdes
- Cau i Estratègies d'Invalidació
Mòdul 8: Arquitectura al Núvol i Desplegament
- Fonaments del Cloud Computing (IaaS, PaaS, SaaS)
- Contenidors i Orquestració amb Docker i Kubernetes
- Arquitectura Serverless
- Patrons de Disseny Cloud-Native
- Infraestructura com a Codi (IaC)
Mòdul 9: Qualitat, Seguretat i Observabilitat
- Escalabilitat: Horitzontal vs Vertical i Balanceig de Càrrega
- Alta Disponibilitat i Tolerància a Fallades
- Seguretat per Disseny i Autenticació/Autorització
- Observabilitat: Logging, Mètriques i Traçabilitat
- Rendiment i Proves de Càrrega
