Amb els principis de disseny com a llista de comprovació, l'equip del Luis s'enfronta a la pregunta pràctica: per on es talla el monòlit? Descompondre no és dibuixar sis caixes amb els noms de les carpetes del repositori. És trobar les costures reals del sistema (els llocs on el codi i les dades ja estan, o poden estar, poc acoblats), decidir una estratègia de tall coherent, triar un ordre d'extracció que minimitzi el risc i aplicar tècniques que permetin extreure peces sense aturar la botiga.

En aquesta lliçó recorrem les estratègies de descomposició (per capacitat de negoci, per subdomini, per casos d'ús enfront de recursos, per equips), les tècniques per descobrir les costures (event storming introductori, anàlisi de dependències del codi i anàlisi d'acoblament de les taules de l'esquema SQL de 01-05), les dues tècniques d'extracció segura més utilitzades (strangler fig i branch by abstraction), els criteris per ordenar l'extracció i què fer amb les utilitats compartides com bd/connexio.js o serveis/correu.js. Acabem amb l'exemple guiat: el monòlit techcorp-shop descompost en els sis serveis del mapa objectiu, amb el diagrama de dependències abans i després. Com es reparteixen físicament les dades (02-04) i com se substitueix la transacció única (02-05) queden per a les lliçons següents.

Contingut

  1. Què significa descompondre (i què no)
  2. Estratègies de descomposició
  3. Tècniques per descobrir les costures
  4. Extreure sense trencar: strangler fig i branch by abstraction
  5. En quin ordre extreure: risc, valor i dependències
  6. Què fer amb les utilitats compartides
  7. Exemple guiat: techcorp-shop en sis serveis

  1. Què significa descompondre (i què no)

Descompondre un monòlit és redistribuir responsabilitats, codi i dades en unitats autònomes que compleixin els principis de 02-01. Tres aclariments eviten malentesos freqüents:

  • No és "un servei per carpeta". Que el monòlit tingui controladors/comandesControlador.js no garanteix que "comandes" sigui una bona frontera: com hem vist, aquesta funció toca cinc àrees. Les carpetes són una pista, no la resposta.
  • No és "un servei per taula". Seria el servei d'entitat de 01-04. Un servei agrupa les taules que canvien juntes sota una capacitat.
  • No és un acte únic. És un procés incremental en què cada extracció deixa el sistema funcionant i aporta valor per si mateixa. Si una extracció només té sentit "quan hi siguin totes", és un big bang disfressat.

El resultat de la descomposició és un pla: quins serveis hi haurà, què s'emporta cadascun del monòlit, en quin ordre s'extreuen i amb quina tècnica. Aquest pla és el lliurable d'aquesta lliçó per a TechCorp.

  1. Estratègies de descomposició

Hi ha quatre maneres habituals de decidir les fronteres. No són excloents: a la pràctica es combinen, i una serveix per validar-ne una altra.

2.1 Per capacitat de negoci

Es pregunta "què fa l'empresa?" i es respon amb una llista de capacitats estables en el temps: gestionar el catàleg, controlar l'inventari, tramitar comandes, cobrar, atendre clients, comunicar-s'hi. Cada capacitat té un responsable de negoci, un vocabulari i un ritme de canvi propis (principi 3 de 02-01).

És l'estratègia més utilitzada perquè les capacitats canvien molt menys que la tecnologia o l'organització: TechCorp cobrava comandes fa deu anys i les cobrarà d'aquí a deu, encara que canviï de passarel·la tres vegades.

2.2 Per subdomini (DDD)

El Domain-Driven Design parteix del domini (el negoci sencer) i el divideix en subdominis: parts del problema amb el seu propi model i el seu propi llenguatge. Sol produir fronteres molt semblants a les capacitats de negoci, però amb dues aportacions addicionals:

  • Distingeix subdominis core (on l'empresa competeix), de suport i genèrics, cosa que ajuda a decidir on invertir esforç de disseny.
  • Detecta que una mateixa paraula ("producte", "client") significa coses diferents en subdominis diferents, i que aquesta diferència de significat és una costura.

En aquesta lliçó fem servir DDD només com a estratègia de tall; la definició rigorosa dels bounded contexts de TechCorp, el llenguatge ubic i el mapa de contextos són el contingut de 02-03.

2.3 Per "verbs / casos d'ús" enfront de "substantius / recursos"

És una decisió de granularitat dins de qualsevol de les anteriors:

Enfocament S'organitza al voltant de... Exemple Quan funciona Risc
Substantius / recursos Entitats del domini: producte, comanda, pagament. servei-comandes és propietari de l'entitat Comanda i de tot el seu cicle de vida. Quan l'entitat té un cicle de vida ric i les operacions sobre ella estan cohesionades. Degenerar en servei d'entitat (CRUD sense negoci).
Verbs / casos d'ús Processos: fer una comanda, retornar una comanda, reposar estoc. servei-checkout que orquestra la compra de principi a fi. Quan un procés creua moltes entitats i mereix un propietari propi. Que el servei "verb" acabi coneixent les dades de tothom (torna el crearComanda de sempre, però per HTTP).

Per a TechCorp l'elecció és substantius com a base (comandes, productes, estoc, pagaments, clients) i el flux "fer una comanda" repartit entre ells mitjançant esdeveniments, sense un servei "checkout" separat. La raó es veurà amb detall a 02-05: el flux es coordina per coreografia i servei-comandes és propietari de l'estat de la comanda, no de les operacions dels altres. Si el flux es compliqués (devolucions parcials, comandes amb diversos enviaments), un servei orquestrador seria el moment de reconsiderar-ho.

2.4 Per equips

De vegades es talla segons qui mantindrà què. És la Llei de Conway feta servir a propòsit (02-01, apartat 9): si Pagaments i Notificacions seran del mateix equip, han de ser un servei o dos? La resposta de TechCorp és dos, perquè tenen ritmes de canvi i perfils de risc diferents (Pagaments toca diners i una passarel·la externa; Notificacions toca plantilles i proveïdors de correu), però és legítim que l'organització influeixi en la granularitat final.

Estratègia Pregunta que respon Fortalesa Debilitat Ús a TechCorp
Capacitat de negoci Què fa l'empresa? Estable en el temps; entenedora per a negoci. Pot ignorar acoblaments tècnics reals. Base del tall.
Subdomini (DDD) On canvia el significat de les paraules? Detecta costures subtils; prioritza el core. Requereix formació de l'equip. Refinament a 02-03.
Verbs vs. substantius Entitats o processos? Ajusta la granularitat. Els "verbs" tendeixen a acaparar dades. Substantius + esdeveniments.
Per equips Qui ho manté? Alinea organització i sistema. Fossilitza l'organització actual. Ajust final de granularitat.

  1. Tècniques per descobrir les costures

Les estratègies diuen com pensar; les tècniques diuen on mirar. TechCorp en fa servir tres, de més conversacional a més mecànica.

3.1 Event storming (nivell introductori)

És un taller en què negoci i tècnics reconstrueixen el domini en una paret amb notes adhesives. En la seva versió mínima:

  1. Esdeveniments de domini (taronja): fets rellevants en passat. "Comanda creada", "Estoc reservat", "Pagament confirmat", "Correu enviat", "Producte publicat", "Preu canviat", "Client registrat", "Estoc reposat".
  2. Ordres (blau): la intenció que provoca l'esdeveniment. "Crear comanda", "Reservar estoc", "Cobrar".
  3. Actors i sistemes externs (groc / rosa): client, operari de magatzem, passarel·la de pagament, proveïdor de correu.
  4. S'ordenen en una línia temporal i es busquen esdeveniments pivot: aquells després dels quals canvia el vocabulari i canvien les persones que parlen.

El que es descobreix per a TechCorp, resumit:

flowchart LR
    subgraph Cataleg[Catàleg]
        E1[Producte publicat] --> E2[Preu canviat]
    end
    subgraph Clients
        E3[Client registrat]
    end
    subgraph Comandes
        E4[Comanda creada] --> E7[Comanda confirmada]
        E4 --> E8[Comanda cancel·lada]
    end
    subgraph Inventari
        E5[Estoc reservat]
        E9[Estoc reposat]
        E10[Estoc alliberat]
    end
    subgraph Pagaments
        E6[Pagament confirmat]
        E11[Pagament reemborsat]
    end
    subgraph Notificacions
        E12[Correu enviat]
    end
    E4 -.-> E5 -.-> E6 -.-> E7 -.-> E12

Els grups d'esdeveniments que comparteixen vocabulari i actor són els candidats a servei; les fletxes discontínues entre grups són els esdeveniments que creuaran fronteres (els comanda.creada, estoc.reservat, pagament.confirmat, comanda.confirmada i comanda.cancellada de 01-05 surten literalment d'aquesta paret). Fixa't que hi apareixen dos esdeveniments que encara no havíem anomenat, "Estoc alliberat" i "Pagament reemborsat": són les compensacions que necessitarà la saga de 02-05. El taller també revela que "Preu canviat" no interessa a Comandes (una comanda desa el preu del moment de la compra), cosa que anticipa una decisió de dades de 02-03.

3.2 Anàlisi de dependències del codi

La segona tècnica és mecànica: qui importa qui dins del repositori del monòlit. Un graf de require/import (eines com madge o dependency-cruiser el generen en segons) mostra els mòduls amb moltes dependències entrants (difícils d'extreure perquè tothom els fa servir) i sortints (difícils d'extreure perquè fan servir tothom).

# A l'arrel del monòlit techcorp-shop: graf de dependències entre mòduls
npx madge --extensions js --json ./ > dependencies.json
# Mòduls amb més dependències entrants (de qui depèn tothom?)
npx madge --extensions js --summary ./

Resum del que veu l'equip del Luis a techcorp-shop:

Mòdul Depèn de (sortints) El fan servir (entrants) Lectura
bd/connexio.js Tots els controladors Utilitat tècnica compartida; tractar segons l'apartat 6.
controladors/comandesControlador.js bd, passarellaPagament, correu rutes/comandes.js Poques dependències de mòdul, però moltíssimes de taules (vegeu 3.3): l'acoblament és a l'SQL, no als require.
controladors/catalegControlador.js bd rutes/cataleg.js Autònom a nivell de codi. Bon primer candidat.
serveis/correu.js comandesControlador, clientsControlador, enviamentsControlador Utilitat amb lògica de negoci a dins (plantilles). Candidata a servei.
serveis/passarellaPagament.js comandesControlador, devolucionsControlador Adaptador extern; se l'emportarà servei-pagaments.

La lliçó d'aquesta taula: en un monòlit amb base de dades compartida, el graf de require enganya. Els mòduls semblen independents perquè l'acoblament real passa per les taules. D'aquí la tercera tècnica.

3.3 Anàlisi d'acoblament de taules

Es construeix una matriu taula × àrea marcant qui llegeix (L) i qui escriu (E) cada taula, a partir del codi SQL del monòlit. Per a l'esquema de 01-05:

Taula Clients Catàleg Inventari Comandes Pagaments Notificacions Propietari natural
clients E L (crearComanda) L (email, nom) Clients
productes E L (FK) L (nom, preu) Catàleg
atributs_producte E Catàleg
estoc E E (crearComanda reserva i descompta) Inventari
comandes E L (comanda_id) L (correu nou) Comandes
linies_comanda L (informe "més venuts") E Comandes
pagaments E (crearComanda insereix) E Pagaments
notificacions E Notificacions

Com es llegeix:

  • Una taula amb una sola E i cap L aliena (atributs_producte, notificacions) és una costura neta: s'extreu amb la seva àrea sense tocar res més.
  • Una taula amb una E pròpia i diverses L alienes (clients, productes, comandes) s'extreu amb el seu propietari; les lectures alienes es converteixen en crides al contracte o en còpies de només lectura (02-04).
  • Una taula amb dues E (estoc, pagaments) és un problema de disseny, no només d'extracció: avui crearComanda escriu a taules d'Inventari i de Pagaments. Abans d'extreure cal retornar l'escriptura al seu propietari (Comandes demanarà a Inventari que reservi, no reservarà ell). Aquesta és la primera refactorització que farà l'equip del Luis dins del monòlit, abans de moure res.

Aquesta matriu és l'eina més valuosa de tota la lliçó: converteix "crec que comandes està molt acoblat" en "comandes escriu a dues taules alienes i en llegeix dues més", que és una cosa sobre la qual es pot planificar.

  1. Extreure sense trencar: strangler fig i branch by abstraction

Un cop decidit què extreure, cal com fer-ho amb la botiga funcionant. Dues tècniques cobreixen la majoria de casos. Aquí les presentem com a eines de descomposició; el relat complet de la migració de TechCorp, pas a pas, és a 08-01.

4.1 Strangler fig (figuera estranguladora)

Es col·loca una façana (a TechCorp, l'API Gateway del port 8080) davant del monòlit. Al principi, tot el trànsit va al monòlit. Quan un servei nou està a punt, la façana redirigeix només les seves rutes al servei; la resta continua anant al monòlit. A poc a poc, el monòlit rep menys rutes fins a quedar buit i apagar-se.

flowchart LR
    C[Client] --> GW[API Gateway :8080]
    GW -- "/productes/*  (ja extret)" --> CAT[servei-cataleg :3001]
    GW -- "/comandes/*, /clients/*, /pagaments/* (encara)" --> MONO[Monòlit techcorp-shop]
    CAT --> M1[(MongoDB catàleg)]
    MONO --> PG[(PostgreSQL techcorp)]

Avantatges: reversible (si el servei nou falla, la façana torna a apuntar al monòlit), incremental i visible per al negoci (cada ruta migrada és una fita). Requisit: la façana ha d'existir abans de la primera extracció, cosa que la converteix en el primer lliurable tècnic del pla.

4.2 Branch by abstraction

Serveix per a les dependències internes que la façana no veu: per exemple, crearComanda obté preus llegint la taula productes. No es pot redirigir "per ruta"; cal canviar el codi del monòlit. La tècnica:

  1. Introduir una abstracció davant de la funcionalitat (una interfície RepositoriProductes).
  2. Fer que el codi existent la faci servir (la implementació inicial continua llegint la taula).
  3. Escriure una segona implementació que faci servir el servei nou (client HTTP de servei-cataleg).
  4. Commutar entre totes dues amb configuració (una feature flag), primer en proves, després en producció, amb marxa enrere immediata si alguna cosa falla.
  5. Esborrar la implementació antiga.
// monolit/comandes/repositoriProductes.js  (branch by abstraction, esquelet)
// Pas 1-2: l'abstracció. crearComanda deixa de fer SQL directe i crida això.
class RepositoriProductesSQL {                  // implementació antiga: taula local
  async obtenirPerIds(ids) {
    const { rows } = await bd.query(
      'SELECT id AS "producteId", nom, preu FROM productes WHERE id = ANY($1) AND actiu', [ids]);
    return rows;
  }
}

class RepositoriProductesHTTP {                 // implementació nova: contracte del servei
  async obtenirPerIds(ids) {
    // El detall del client HTTP (timeouts, reintents) es veu a 04-04; aquí només el contracte.
    return await catalegClient.get('/productes', { ids: ids.join(',') });
  }
}

// Pas 4: la commutació per configuració. Mateix contracte, dos orígens.
function crearRepositoriProductes(config) {
  return config.CATALEG_REMOT === 'true'
    ? new RepositoriProductesHTTP()
    : new RepositoriProductesSQL();
}

module.exports = { crearRepositoriProductes };

Fixa't que totes dues implementacions retornen la mateixa forma (producteId, nom, preu): aquesta forma és l'embrió del contracte del catàleg, i fer-la explícita dins del monòlit ja és un pas de descomposició encara que no s'hagi mogut ni una sola línia de servidor.

Tècnica Actua sobre Reversible Serveix per a
Strangler fig Trànsit d'entrada (rutes HTTP) Sí (canvi d'encaminament) Extreure funcionalitats exposades a l'exterior.
Branch by abstraction Dependències internes del codi Sí (feature flag) Substituir un col·laborador intern per un servei remot.

  1. En quin ordre extreure: risc, valor i dependències

No tots els serveis s'extreuen alhora ni en qualsevol ordre. TechCorp puntua cada candidat en tres eixos:

  • Valor: quant alleuja un problema real dels cinc de 01-05 (escalat en campanyes, desplegaments, incidents que es propaguen, model de dades forçat, equips que es trepitgen).
  • Risc: quant negoci es posa en joc si l'extracció surt malament, i quanta lògica delicada (diners, identitat) es toca.
  • Dependències: quantes àrees en depenen (entrants) i de quantes depèn (sortints). Poques dependències, extracció fàcil.
Servei Valor Risc Dependències entrants / sortints Puntuació i ordre
servei-cataleg Molt alt: és el que se satura ×20 en campanyes i el que pateix el model clau-valor. Baix: és de lectura majoritària; si falla, la botiga mostra un error de cerca, no perd cobraments. Entrants: Comandes (nom i preu). Sortints: cap. 1r
servei-inventari Alt: escala amb les campanyes i és el primer pas de la saga. Mitjà: un error d'estoc ven el que no hi ha. Entrants: Comandes. Sortints: cap (només necessita producteId). 2n
servei-notificacions Alt: és l'origen de la fuita de memòria que va tombar els cobraments; treure'l aïlla l'incident. Baix: si falla, s'endarrereix un correu. Entrants: Comandes, Clients. Sortints: llegeix clients i comandes (se substitueix per dades a l'esdeveniment). 3r
servei-pagaments Alt: aïlla la passarel·la externa i la seva latència; permet acotar l'abast PCI. Alt: diners. Entrants: Comandes. Sortints: passarel·la externa. 4t
servei-comandes Molt alt: és el core, el que més canvia i el que el Luis vol desplegar cada dia. Alt: és el flux de venda. Entrants: Notificacions, Pagaments, informes. Sortints: totes (clients, catàleg, inventari, pagaments). : s'extreu quan els seus col·laboradors ja existeixen com a serveis i la saga està dissenyada.
servei-clients Mitjà: l'autenticació es delega a Keycloak, així que el monòlit ja s'alleugereix sense extreure'l. Alt: identitat i dades personals; client_id és a tot arreu. Entrants: totes les àrees. Sortints: cap. : l'últim; mentrestant, el monòlit residual serveix /clients.

Tres raons resumeixen per què el catàleg va primer: és on hi ha el problema més car (les caigudes de campanya), és l'extracció amb menys risc (no toca diners ni el flux de comanda al seu camí crític) i no depèn de ningú: només Comandes en depèn per a dos camps, i això es resol amb l'abstracció de l'apartat 4.2. Extreure'l primer dona a més una victòria primerenca que justifica la inversió davant de la Marta i entrena l'equip de plataforma en el pipeline complet (Docker, Kubernetes, canary) amb el servei menys perillós.

I una raó per la qual comandes no va primer encara que sigui el core: extreure primer el servei que depèn de tots obligaria a construir sis contractes alhora o que servei-comandes continués llegint la base de dades del monòlit, és a dir, a néixer com a monòlit distribuït.

  1. Què fer amb les utilitats compartides

Tot monòlit té codi que "fa servir tothom". A techcorp-shop: bd/connexio.js, serveis/correu.js, serveis/passarellaPagament.js, a més d'utilitats menors (registre de logs, validació de peticions, format d'errors). Hi ha quatre destinacions possibles, i triar malament crea acoblament nou:

Utilitat Naturalesa Destinació recomanada Per què
bd/connexio.js (pool de PostgreSQL) Tècnica, sense negoci, 30 línies Copiar a cada servei (o plantilla de projecte). Cada servei té la seva pròpia BD i la seva pròpia configuració; una llibreria compartida per a 30 línies acobla els desplegaments per no res.
Registre de logs, format d'errors HTTP, validació de JSON Tècnica, sense negoci Llibreria interna versionada (@techcorp/comu-http), publicada en un registre privat, amb versions semàntiques. S'accepta llibreria compartida només per a codi sense lògica de negoci; cada servei tria quan actualitzar la versió, així que no hi ha acoblament de desplegament.
serveis/correu.js (plantilles, enviament SMTP) Conté negoci (quins correus existeixen, què diuen) Es converteix en el nucli de servei-notificacions. No es comparteix. Si fos llibreria, cada servei enviaria els seus correus i la fuita de memòria continuaria dins de tots ells. Com a servei, reacciona a esdeveniments i aïlla la fallada.
serveis/passarellaPagament.js (adaptador de la passarel·la) Adaptador extern amb regles de cobrament Se l'emporta servei-pagaments en exclusiva. Només un servei ha de parlar amb la passarel·la; la resta demana "cobrar" per contracte.
Models / DTO de negoci (models/Comanda.js, models/Producte.js) Negoci No es comparteixen mai. Cada servei defineix els seus. Compartir models de domini és la "capa compartida" de 01-04: un canvi a Producte redesplega tothom. Cada context té el seu propi "producte" (02-03).

La regla que TechCorp adopta: es comparteix codi tècnic com a llibreria versionada; el codi de negoci no es comparteix, es converteix en un servei o es duplica conscientment. Duplicar un DTO de 5 camps en dos serveis és barat; compartir-lo costa l'autonomia de tots dos.

  1. Exemple guiat: techcorp-shop en sis serveis

Apliquem tot l'anterior. El pla de descomposició de TechCorp:

Pas 1. Inventari del monòlit. Carpetes, mòduls i taules (01-05), i matriu taula × àrea (3.3).

Pas 2. Tall per capacitat de negoci, validat amb els grups d'esdeveniments de l'event storming (3.1) i amb la matriu de taules: sis capacitats, sis serveis. Coincideix amb les sis àrees funcionals, amb dos matisos que la matriu ha destapat: la reserva d'estoc la farà Inventari (avui la fa crearComanda) i el registre del pagament el farà Pagaments (avui el fa crearComanda).

Pas 3. Repartiment de codi i dades (les dades es detallen a 02-04; aquí, l'assignació):

Servei S'emporta del monòlit Taules de què serà propietari Deixa de fer
servei-cataleg (3001) controladors/catalegControlador.js, rutes/cataleg.js, cerca productes, atributs_producte (que a 02-04 es convertiran en documents de MongoDB)
servei-inventari (3006) Lògica de reserva/alliberament avui repartida entre crearComanda i magatzemControlador.js estoc (+ nova taula de reserves)
servei-comandes (3002) comandesControlador.js, rutes/comandes.js comandes, linies_comanda Llegir clients i productes; escriure estoc i pagaments; enviar correu.
servei-pagaments (3003) serveis/passarellaPagament.js, devolucionsControlador.js pagaments
servei-notificacions (3005) serveis/correu.js, plantilles Registre mínim d'enviaments (notificacions) Llegir clients i comandes (rebrà el que necessita als esdeveniments).
servei-clients (3004) clientsControlador.js, perfil, adreces clients Autenticar (es delega a Keycloak, 07-01).

Pas 4. Diagrama de dependències abans i després.

Abans: tots els mòduls depenen de totes les taules a través d'un únic pool de connexió. El graf és, a la pràctica, complet.

flowchart TB
    subgraph MONO[Monòlit techcorp-shop - un procés, un desplegament]
        PC[comandesControlador]
        CC[catalegControlador]
        CLC[clientsControlador]
        AC[magatzemControlador]
        DC[devolucionsControlador]
        CO[serveis/correu]
        PP[serveis/passarellaPagament]
        PC --> CO
        PC --> PP
        DC --> PP
        CLC --> CO
    end
    subgraph PG[(PostgreSQL techcorp - un esquema)]
        T1[clients]
        T2[productes]
        T3[estoc]
        T4[comandes / linies_comanda]
        T5[pagaments]
        T6[notificacions]
    end
    PC --> T1
    PC --> T2
    PC --> T3
    PC --> T4
    PC --> T5
    CC --> T2
    CC --> T4
    CLC --> T1
    AC --> T3
    AC --> T2
    DC --> T5
    DC --> T4
    CO --> T6
    CO --> T1

Després: cada servei depèn només del seu emmagatzematge; entre serveis només hi ha contractes (fletxes contínues: crides síncrones a l'API) i esdeveniments (fletxes discontínues: publicació/subscripció). El com d'aquestes fletxes és el mòdul 3.

flowchart TB
    GW[API Gateway :8080] --> CAT[servei-cataleg :3001]
    GW --> CLI[servei-clients :3004]
    GW --> PED[servei-comandes :3002]
    CAT --> BDC[(MongoDB catàleg)]
    CLI --> BDL[(PG clients)]
    PED --> BDP[(PG comandes)]
    INV[servei-inventari :3006] --> BDI[(PG inventari)]
    PAG[servei-pagaments :3003] --> BDG[(PG pagaments)]
    NOT[servei-notificacions :3005]
    PED -- "GET /productes (nom, preu)" --> CAT
    PED -- "GET /clients/{id} (existeix, email)" --> CLI
    PED -. "comanda.creada / comanda.confirmada / comanda.cancellada" .-> MQ[(RabbitMQ)]
    MQ -. "comanda.creada" .-> INV
    INV -. "estoc.reservat" .-> MQ
    MQ -. "estoc.reservat" .-> PAG
    PAG -. "pagament.confirmat" .-> MQ
    MQ -. "pagament.confirmat" .-> PED
    MQ -. "comanda.confirmada / comanda.cancellada" .-> NOT
    PAG --> EXT1[Passarel·la externa]
    NOT --> EXT2[Correu / SMS]

Compara els dos grafs amb la taula d'acoblaments de 02-01: al primer, tot és acoblament de dades i de desplegament; al segon, només hi ha acoblament de contracte (dues crides síncrones de Comandes) i d'esdeveniments. Queda un acoblament temporal deliberat (Comandes consulta Catàleg i Clients de manera síncrona en crear la comanda); a 02-04 veurem l'alternativa de replicar aquestes dades i a 06-03 com protegir aquestes crides.

Pas 5. Ordre i tècnica d'extracció. Catàleg (strangler per rutes /productes + branch by abstraction per als preus a crearComanda), Inventari (branch by abstraction per a la reserva), Notificacions (el monòlit comença a publicar esdeveniments i el servei els consumeix), Pagaments (branch by abstraction per a passarellaPagament), Comandes (strangler de /comandes, ja amb la saga de 02-05) i Clients (strangler de /clients; el monòlit s'apaga).

Errors Comuns i Consells

  • Tallar per carpetes del repositori. Les carpetes reflecteixen com es va organitzar el codi fa anys, no les costures de negoci. Fes servir la matriu de taules: no menteix.
  • Refiar-se només del graf de require. En un monòlit amb BD compartida, l'acoblament és a l'SQL. Un mòdul sense dependències de codi pot escriure a quatre taules alienes.
  • Extreure el core primer "perquè és l'important". És el que depèn de tots; extreure'l primer obliga a néixer acoblat. Comença pel que no depèn de ningú i aporta valor visible.
  • Convertir les utilitats en una gran llibreria compartida. Cada actualització de la llibreria redesplega tothom: és acoblament de desplegament en estat pur. Només codi tècnic, versionat, i cada servei decideix quan actualitzar.
  • Extreure sense façana. Sense gateway no hi ha strangler fig ni marxa enrere barata. El gateway és la primera peça, no l'última.
  • Consell: abans de moure codi a un servei nou, arregla l'acoblament dins del monòlit (retorna les escriptures al seu propietari, introdueix les abstraccions). Un monòlit ben modularitzat es descompon en setmanes; un d'embolicat, en anys.

Exercicis

Exercici 1: Llegir la matriu

Amb la matriu taula × àrea de l'apartat 3.3: (a) quines dues taules exigeixen una refactorització dins del monòlit abans de poder extreure'n el servei, i en què consisteix? (b) Quina taula és la d'extracció més neta i per què? (c) Notificacions llegeix clients i comandes; com obtindrà aquestes dades quan sigui un servei, sense llegir taules alienes?

Exercici 2: Un setè servei

La Marta vol afegir "promocions" (cupons de descompte, el cas de 01-03) durant la migració. Aplica els criteris de valor, risc i dependències i decideix: es construeix directament com a servei-promocions o s'afegeix primer al monòlit? En quina posició de l'ordre d'extracció encaixaria? Justifica-ho en 5-8 línies.

Exercici 3: Branch by abstraction per a la reserva d'estoc

Escriu l'esquelet (sense implementar detalls) de l'abstracció que permetria a crearComanda deixar d'escriure a estoc i passar a demanar la reserva a servei-inventari, amb commutació per configuració. Defineix el contracte de l'abstracció (nom del mètode, entrada, sortida) i indica què retornaria cada implementació quan no hi ha estoc.

Solucions

Exercici 1

(a) estoc i pagaments: totes dues tenen dos escriptors. crearComanda reserva i descompta estoc, i insereix a pagaments. Abans d'extreure Inventari i Pagaments, cal moure aquesta lògica als seus mòduls propietaris dins del monòlit (per exemple, magatzem.reservar(...) i pagaments.registrarCobrament(...)), de manera que crearComanda només invoqui i no escrigui. Després, aquestes invocacions se substitueixen per crides al servei amb branch by abstraction. (b) atributs_producte (i notificacions): un sol escriptor, cap lector aliè. S'extreu amb Catàleg sense que ningú més se n'assabenti; a més, el seu model clau-valor és la raó per portar el catàleg a MongoDB. (c) Rebent-les a l'esdeveniment: comanda.confirmada inclourà el comandaId, el total i les dades mínimes del client necessàries per al correu (email, nom) que servei-comandes haurà obtingut per contracte de Clients. Alternativament, Notificacions podria consultar GET /clients/{id} en rebre l'esdeveniment; totes dues opcions es comparen a 02-04. El que no farà mai és SELECT sobre clients.

Exercici 2

Valor: mitjà-alt per a negoci (campanyes), però no resol cap dels cinc problemes de 01-05. Risc: baix en si mateix, però alt pel moment: afegir un servei nou mentre es migra el core duplica el front de treball. Dependències: promocions necessitaria conèixer productes (a què s'aplica) i comandes (on s'aplica), és a dir, depèn de dos serveis que encara no estan extrets. Decisió raonable: construir-lo dins del monòlit com un mòdul ben aïllat (la seva pròpia taula cupons, una abstracció Promocions.calcularDescompte(linies) que faci servir crearComanda), respectant des del primer dia els principis de 02-01 perquè la seva extracció posterior sigui trivial. Encaixaria en l'ordre entre Pagaments i Comandes (posició 4-5): després que existeixin Catàleg i Inventari (les seves dependències) i abans que Comandes s'extregui amb la saga definitiva, perquè el camp descompteAplicat de comanda.confirmada neixi ja al contracte. Si el negoci no ho necessita amb urgència, encara millor: després de Comandes, amb el sistema estable.

Exercici 3

// Contracte de l'abstracció: reservar(comandaId, linies) -> { ok: true, reservaId }
//                                                         | { ok: false, motiu: 'SENSE_ESTOC', producteId }
class ReservaEstocSQL {                    // implementació antiga: taula local del monòlit
  async reservar(comandaId, linies) {
    // UPDATE estoc SET reservat = reservat + quantitat WHERE ... AND quantitat - reservat >= quantitat
    // Si algun UPDATE afecta 0 files -> retornar { ok: false, motiu: 'SENSE_ESTOC', producteId }
    // Si tot va bé -> { ok: true, reservaId: `local-${comandaId}` }
  }
  async alliberar(reservaId) { /* UPDATE estoc SET reservat = reservat - quantitat ... */ }
}

class ReservaEstocHTTP {                   // implementació nova: contracte de servei-inventari
  async reservar(comandaId, linies) {
    // POST /reserves { comandaId, linies } -> 201 { reservaId }  o  409 { motiu: 'SENSE_ESTOC', producteId }
    // Retorna la mateixa forma que la implementació SQL.
  }
  async alliberar(reservaId) { /* DELETE /reserves/{reservaId} */ }
}

function crearReservaEstoc(config) {
  return config.INVENTARI_REMOT === 'true' ? new ReservaEstocHTTP() : new ReservaEstocSQL();
}

L'essencial: les dues implementacions retornen la mateixa forma en èxit i en fallada ({ ok, reservaId } / { ok: false, motiu }), de manera que crearComanda no distingeix d'on ve la reserva; i l'abstracció ja inclou l'operació inversa (alliberar), perquè tan bon punt la reserva surti de la transacció única, caldran compensacions (02-05).

Conclusió

Descompondre un monòlit és una feina d'anàlisi abans que de codi. Hem vist les estratègies (capacitat de negoci com a base, subdominis de DDD com a refinament, substantius enfront de verbs per ajustar la granularitat, equips com a últim ajust), les tècniques per descobrir costures (event storming, graf de dependències del codi i, la més reveladora en un monòlit amb BD compartida, la matriu d'acoblament taula × àrea, que ha destapat que crearComanda escriu a estoc i pagaments sense ser-ne el propietari), les tècniques d'extracció segura (strangler fig darrere del gateway i branch by abstraction amb commutació per configuració) i els criteris d'ordre (valor, risc, dependències) que col·loquen el catàleg primer i comandes i clients al final. També hem decidit la destinació de les utilitats compartides: codi tècnic com a llibreria versionada; codi de negoci com a servei (correu.jsservei-notificacions, passarellaPagament.jsservei-pagaments) o duplicat a consciència. El resultat és el pla de TechCorp: sis serveis, cadascun amb els seus mòduls i les seves taules, i un graf de dependències que ha passat de "tots contra totes les taules" a "contractes i esdeveniments".

Aquest pla té encara les fronteres dibuixades a traç gruixut. La lliçó següent les afina amb DDD estratègic: definirem els bounded contexts de TechCorp, veurem per què "producte" i "client" no signifiquen el mateix a cada context, dibuixarem el mapa de contextos amb els seus patrons de relació i decidirem què és un agregat (i per què l'estoc no forma part de l'agregat Comanda).

Curs de Microserveis

Mòdul 1: Introducció als Microserveis

Mòdul 2: Disseny de Microserveis

Mòdul 3: Comunicació entre Microserveis

Mòdul 4: Implementació de Microserveis

Mòdul 5: Desplegament i Orquestració

Mòdul 6: Monitoratge i Manteniment

Mòdul 7: Seguretat en Microserveis

Mòdul 8: Casos d'Estudi i Exemples Pràctics

© Copyright 2026. Tots els drets reservats