Frameworks a la pràctica
- Llegir un projecte
- Cal un framework?
- Triar un framework
- Sobreviure a un canvi de framework
- Peces substituïbles
- Posar ordre en un framework prim
- Muntar-lo amb llibreries
- Referències
Arquitectura dels frameworks dona el vocabulari; aquí el fem servir. Es dona per sabut: peces comunes, model d’execució, eix d’opinió, capes i patró de mapatge.
Serveix per a dues feines, que són la mateixa habilitat mirada des de dos costats: entendre un projecte que ja existeix i decidir-ne un de nou. Un cop triat el framework, en queden tres preguntes més que et faràs segur: què passa si algun dia el vull canviar, quines peces puc deixar preparades per substituir-les, i com m’organitzo si el framework no m’organitza res.
Llegir un projecte
Sis preguntes, en aquest ordre, t’orienten en gairebé qualsevol projecte que no hagis escrit:
- On és el punt d’entrada? El fitxer d’arrencada diu quin framework hi ha i com es configura.
- On és la taula de rutes? Dona el mapa de tot el que l’aplicació sap fer.
- On viu la lògica de negoci? Si és dins dels controladors, no hi ha capa de serveis. Si és dins dels models, hi ha un domain model.
- Quin patró de persistència fa servir? Determina quant costarà aïllar el domini.
- Qui construeix les dependències? Un contenidor, una factoria, o cada classe les seves.
- Serveix HTML o serveix dades? Diu si la presentació viu aquí o en un projecte a part, i si has de buscar la meitat de l’aplicació en un altre repositori.
Si les sis respostes són fàcils de trobar, el projecte té una arquitectura. Si costen, ja saps què t’espera.
Cal un framework?
Gairebé sempre sí, i val la pena saber per què. Sense framework has d’escriure tu l’encaminament, la interpretació de la petició, les sessions, l’autenticació, la protecció contra CSRF, la negociació de continguts, la connexió amb la base de dades i les migracions. Cap d’aquestes peces és interessant, i unes quantes són de seguretat: l’escapament de plantilles, la protecció CSRF o les consultes preparades del framework les han mirat milers de persones, i la teva versió no. Disseny segur hi insisteix: aquí no es guanya res reinventant.
L’argument de fons és que no fer servir cap framework no vol dir no tenir-ne, sinó tenir-ne un de propi, sense documentació, sense comunitat i sense ningú que l’hagi provat. La pregunta útil, per tant, no és si en vols un, sinó si en vols un de compartit.
Els casos on la resposta és que no en cal cap són pocs i es reconeixen fàcilment: un script d’un sol punt d’entrada, una tasca programada, una biblioteca, o un entorn molt limitat. I un matís: a Go, “sense framework” vol dir amb la llibreria estàndard, que ja porta un servidor HTTP complet. Es tracta d’una frontera molt més borrosa del que sembla. Si ets en aquest cas, l’última secció diu com muntar-lo amb llibreries sense acabar amb un framework accidental.
Triar un framework
Les sis preguntes de Llegir un projecte serveixen quan el codi ja existeix; per triar, el problema és que costa respondre-les abans d’haver construït res. Va millor començar per la resposta per defecte i buscar motius per apartar-se’n.
La resposta per defecte: un framework opinionat, en el llenguatge que l’equip ja sap. Per a la immensa majoria de projectes (usuaris, formularis, una base de dades i uns quants informes) és l’opció correcta, i tota la resta d’aquesta secció són raons per no seguir-la.
Cada decisió té un senyal observable que et permet respondre-la sense endevinar res:
- Quantes decisions vols que prengui per tu? El senyal és quanta gent i quant temps. Si hi treballarà més d’una persona, o ha de durar més enllà d’unes setmanes, les convencions compartides es paguen soles. El microframework només guanya clarament quan el servei fa una sola cosa i la fa sempre igual.
- Què ha de servir? El senyal és qui és el client. Si l’únic client és un navegador i les pantalles són formularis i llistes, HTML des del servidor. Si hi ha una app mòbil, més d’un client, o pantalles que han de reaccionar sense recarregar, API i SPA. Compte amb aquest punt, perquè és el que més sovint es decideix per moda: si no saps dir en veu alta per què vols una SPA, no la necessites.
- Quin patró de mapatge et deixa? El senyal és si pots escriure les regles de negoci en una frase. Si totes les regles són “valida això i desa-ho”, Active Record va perfecte. Si hi ha invariants que s’han de complir sempre i te’ls pots imaginar vivint dins d’una classe, vols un Data Mapper, i val la pena comprovar-ho abans de triar el framework, no després.
- Quin model d’execució necessites? El senyal és què fa el servidor mentre espera. Si el temps se’n va esperant la base de dades o serveis externs i esperes molta concurrència, un entorn asíncron hi va a favor. Si la feina és de càlcul, o la concurrència és modesta, un fil per petició. En cas de dubte, un fil per petició: és el més fàcil de depurar quan alguna cosa va malament.
- Quin mecanisme de separació necessites? El senyal és quants equips i quants ritmes de desplegament hi ha. Un sol desplegable, amb mòduls a dins, és el punt de partida raonable gairebé sempre: són les apps de Django, els mòduls de NestJS o els starters de Spring, i és exactament el que necessita un monòlit modular. Els microserveis valen la pena quan hi ha equips que han d’anar per separat o parts que han d’escalar per separat, no abans.
Sobreviure a un canvi de framework
Vist tot el que el framework decideix per tu, la pregunta següent surt sola: es pot escriure una aplicació que no hi depengui? Es pot, i gairebé sempre és mal negoci. Una capa de traducció que aïlli el framework sencer es paga cada dia, a canvi d’una migració que la major part de projectes no faran mai. És la mateixa conclusió que a Active Record vs Data Mapper: anar en contra del framework té un cost.
Ara bé, n’hi ha una versió barata que sí que compensa, i el motiu per fer-la no és la migració: que les regles de negoci visquin en fitxers que no importen el framework. Prova de fer-ho i comprovaràs que el benefici arriba de seguida, molt abans de cap canvi de framework:
- Les proves d’aquestes regles no necessiten ni servidor ni base de dades, que és el senyal que buscàvem a Testabilitat.
- Les regles es poden llegir sense saber el framework, que és el que permet que algú nou hi entri.
- I si algun dia canvieu de framework, el canvi toca les vores i no el cor.
El millor d’aquesta regla és que es pot comprovar sola: si al directori del domini hi apareix un
import del framework, s’ha trencat. És literalment una
funció d’aptitud, i n’hi ha prou
amb una prova que grepi els imports.
El que sí que val la pena tenir clar és què és portable i què no:
- Portables: les regles de negoci, els càlculs, les decisions. Són codi del teu llenguatge i prou.
- No portables, i no ho intentis: controladors, mapatge de l’ORM, plantilles, configuració, autenticació. Són precisament el que el framework et dona fet, i escriure-hi una capa d’abstracció al damunt vol dir mantenir dues coses en lloc d’una.
Perquè l’expectativa sigui realista: l’objectiu no és canviar de framework en un cap de setmana, sinó que, quan es canviï, les regles sobrevisquin. I recorda que no totes les decisions costen igual de desfer. El llenguatge, a efectes pràctics, no es desfà. El framework dins d’un mateix llenguatge és una migració cara però acotada. L’ORM fa mal, però es pot fer per parts. L’estructura de capes és barata i, de fet, canviarà sola a mesura que entenguis millor el problema, així que no val la pena discutir-la gaire abans de començar.
Peces substituïbles
Preguntar si es pot canviar “el framework” és preguntar-ho a la granularitat equivocada. A la pràctica no se substitueix mai tot de cop: se substitueix la base de dades, o el motor de plantilles, o el client d’un servei extern. La unitat útil és la peça, i el mecanisme ja el coneixem, perquè és el dels ports i adaptadors que l’altra pàgina va anar traduint al vocabulari de cada framework.
La recepta és la mateixa per a qualsevol peça que vulguis poder canviar:
- Anomena la frontera amb el teu vocabulari, no amb el de la tecnologia de sota. Una interfície
amb
desaComandaicercaPerClient, no ambexecutaQuery. - Comprova que no la travessi cap tipus de l’altre costat. És el criteri decisiu i és fàcil de mirar.
- Implementa-la dues vegades. Encara que la segona sigui un doble per a les proves. Un port que només s’ha implementat un cop és una hipòtesi, no una frontera.
La persistència és el cas de manual. La peça substituïble és el repositori, i la prova que
funciona és mirar què retorna: si retorna entitats de l’ORM, objectes mandrosos que encara
consulten, o un QuerySet o una Session, la base de dades s’ha escapat cap amunt i el port no
existeix. Si retorna objectes teus, pots canviar de motor, passar de l’ORM a SQL directe, posar-hi
una memòria cau al davant o provar-ho tot amb una implementació en memòria. Aquí torna a aparèixer
el peatge de l’Active Record: com que l’entitat és l’ORM, un port de debò vol dir mantenir un
segon joc d’objectes. Amb un Data Mapper, o amb un framework prim on l’ORM l’has triat tu, surt molt
més barat.
El frontend funciona diferent, perquè el que el fa substituïble no és una interfície sinó el contracte, i això ja ho hem vist a Qui decideix el contracte. El que hi afegim aquí és què fer si el contracte te l’imposa el framework i no vols quedar-t’hi tancat: la resposta no és afegir capes, sinó mantenir els controladors prims. Si un controlador només tradueix entre la petició i el servei, afegir-hi més endavant una representació JSON al costat de l’HTML és barat; si hi ha regles de negoci a dins, no ho serà mai.
Tot això és més fàcil amb un framework prim, i per dos motius. Perquè no et ve res donat, i per tant la frontera l’has de dibuixar explícitament en lloc d’heretar-la. I perquè les peces que un framework opinionat ja ocupa, l’ORM i les plantilles, allà les tries tu. La contrapartida, i convé dir-la, és que en un framework opinionat val més triar les batalles: fer substituïble la persistència sol valer la pena, i fer substituïble el sistema de plantilles gairebé mai.
Posar ordre en un framework prim
Si tries un microframework, les decisions que un framework opinionat ja té preses passen a ser teves, i el dia u. No són moltes, i aquesta és la llista:
- On van els fitxers. Per funcionalitat o per capa, tant se val, però decidiu-ho i escriviu-ho.
- On viu la lògica de negoci. Un mòdul de serveis que no importi res del framework HTTP. És la regla de la secció anterior, aplicada des del principi.
- Qui construeix les dependències. Sense contenidor, et toca escriure el composition root a mà: un sol fitxer que crea la connexió, els repositoris i els serveis, i els passa a les rutes. És poca feina i evita que cada mòdul s’importi les seves dependències pel seu compte, que és el que després impedeix provar res.
- Com es gestionen els errors. Un únic middleware al final de la cadena que tradueixi els errors de domini a codis HTTP, i enlloc més.
- On es valida l’entrada. A la frontera, abans d’entrar al domini.
- Quin ORM i quin patró de mapatge. Aquí la tria torna a ser teva, amb les conseqüències que descriu Active Record vs Data Mapper.
Fixa’t que aquesta llista és, punt per punt, el que Django o Spring ja porten decidit. Per això la llibertat d’un microframework només és llibertat de veritat si les decisions es prenen una vegada i queden escrites, per exemple en un ADR. Si no, cadascú en pren de diferents i el projecte acaba tenint totes les respostes alhora.
Muntar-lo amb llibreries
Queda el cas que la primera secció donava per poc freqüent: has decidit que no vols framework i vols construir l’arquitectura tu, ajuntant llibreries. És una decisió legítima quan el servei fa una sola cosa, quan l’arrencada i la mida del binari són crítiques (serverless, sistemes encastats) o quan el llenguatge hi convida, com Go. Però recorda la trampa de la primera secció: no en tenir cap vol dir tenir-ne un de propi, així que val més muntar-lo a consciència.
Fes la llista de peces abans d’escriure res. Un framework és, essencialment, aquesta llista ja resolta. Si te la fas tu, no te’n descuidaràs cap a mitges:
| Peça | Què has de decidir |
|---|---|
| Servidor HTTP i encaminament | Sovint ja el porta la llibreria estàndard |
| Validació de l’entrada | Quina llibreria i on s’aplica |
| Accés a dades | Controlador, generador de consultes o ORM |
| Migracions | Eina i on viuen els fitxers |
| Registre (logs) | Format estructurat des del primer dia |
| Configuració | D’on surt i com es valida en arrencar |
| Autenticació | Resum de contrasenyes i format de token |
| Gestió d’errors | Com es tradueixen a codis HTTP |
Segueix la mateixa cadena que faria un framework. Sense middleware integrat, la fas a mà: una funció que n’embolcalla una altra. Amb tres embolcalls (registre, autenticació i errors) ja tens la cadena de responsabilitats de qualsevol framework, i el pas 4 de la secció anterior segueix valent.
Escriu el composition root de veritat. És l’única peça que no et donarà cap llibreria i la que decideix si el projecte serà provable: un fitxer que crea la connexió, els repositoris i els serveis, i els lliga a les rutes.
No escriguis mai tu el resum de contrasenyes, la signatura de tokens, la generació d’identificadors de sessió, l’escapament d’HTML ni la construcció de SQL per concatenació. Són el mateix consell de Disseny segur, i aquí es concentra tot el risc de la decisió: el framework te’ls donava resolts i ara els has de portar tu.
Assumeix el manteniment. Deu llibreries són deu calendaris d’actualitzacions i deu llistes d’avisos de seguretat que ara mires tu. Val la pena triar-les avorrides i molt usades, fixar-ne les versions i posar una anàlisi de dependències a la canonada.
I para atenció al senyal d’aturada. Quan el codi de connexió comença a tenir registre de rutes, convencions de noms i utilitats genèriques, és que estàs escrivint un framework. Aleshores hi ha dues sortides bones, i totes dues passen per admetre-ho: adoptar-ne un de fet, o extreure el teu com a llibreria interna, amb documentació i un ADR que expliqui per què existeix.