Els patrons vistos fins ara afinen relacions entre poques peces: dues interfícies que no encaixen, dues jerarquies, un arbre, una ceba de capes. Facade opera a una altra escala: quan un subsistema sencer —mitja dotzena de serveis amb el seu ordre, les seves regles i els seus errors— ha de ser usat per molts clients, algú ha de saber orquestrar-lo. La pregunta de disseny és: aquest coneixement viu repetit a cada client, o viu una sola vegada darrere d'una porta simple? A PideYa la resposta la demana a crits el checkout: confirmar una comanda exigeix avui que cada aplicació client coordini cinc subsistemes a mà.

Contingut

  1. El problema a PideYa: el checkout escampat
  2. Intenció i estructura del patró
  3. Implementació Java: FacanaCheckout
  4. Què NO és una façana
  5. Façanes per capes i en APIs públiques
  6. Quan usar-lo i quan no
  7. Errors comuns, exercicis i conclusió

El problema a PideYa: el checkout escampat

Confirmar una comanda a PideYa involucra, en ordre, amb gestió d'errors a cada pas:

  1. Validar la cistella: plats disponibles (la carta Composite sap respondre), quantia mínima del restaurant, adreça dins de la zona de repartiment.
  2. Calcular impostos: amb la CalculadoraImpostos del mercat correcte — la família per país que vam blindar amb Abstract Factory.
  3. Cobrar: amb la PassarellaPagament del mercat (Redsys, Conekta o l'AdaptadorPayPal); si el pagament falla, no ha de quedar rastre de la comanda.
  4. Crear i persistir la comanda: muntant-la amb el Comanda.Builder i desant-la al repositori.
  5. Notificar: al client (confirmació) i al restaurant (nova comanda), via ServeiNotificacions.

Aquest coneixement —què cridar, en quin ordre, què fer si el pas 3 falla després del 2— avui està copiat a cada client: l'app iOS, l'app Android, la web, el lloc d'atenció telefònica. Les conseqüències són les esperables:

  • Duplicació amb divergència: la web valida la quantia mínima abans dels impostos; Android, després. Un arranjament al flux cal repetir-lo per quadruplicat, i alguna còpia sempre queda enrere.
  • Acoblament massiu: quatre clients coneixen cinc subsistemes cadascun (20 dependències); canviar la firma d'un servei intern sacseja totes les aplicacions.
  • Coneixement a la capa equivocada: la regla "si el cobrament falla, alliberar la cistella i no notificar" és negoci pur, i viu en codi d'interfície d'usuari.

El primer instint podria ser "simplifiquem els serveis". Però els serveis estan bé: cadascun fa una cosa (SRP) i té la seva complexitat legítima. El que sobra no és complexitat de les peces: és coneixement de la coordinació escampat pels clients.

Intenció i estructura del patró

Intenció (GoF): proporcionar una interfície unificada a un conjunt d'interfícies d'un subsistema. Facade defineix una interfície de més alt nivell que fa el subsistema més fàcil d'usar.

La solució és gairebé anticlimàtica de pur simple: una classe que ofereix les operacions d'alt nivell que els clients de debò necessiten ("confirmar aquesta cistella amb aquest mètode de pagament") i que per dins coneix i coordina el subsistema. Tota la sofisticació del patró és a decidir bé aquesta interfície d'alt nivell, no en la seva mecànica.

classDiagram
    class FacanaCheckout {
        +confirmarComanda(cistella: Cistella, dadesPagament: DadesPagament) ConfirmacioComanda
    }
    class ValidadorCistella { +validar(cistella) }
    class CalculadoraImpostos { <<interface>> +calcular(base) BigDecimal }
    class PassarellaPagament { <<interface>> +cobrar(quantia, dades) ResultatPagament }
    class RepositoriComandes { +desar(comanda) }
    class ServeiNotificacions { +notificarConfirmacio(comanda) }
    class AppMobil
    class WebPideYa
    class AtencioTelefonica

    AppMobil --> FacanaCheckout
    WebPideYa --> FacanaCheckout
    AtencioTelefonica --> FacanaCheckout
    FacanaCheckout --> ValidadorCistella
    FacanaCheckout --> CalculadoraImpostos
    FacanaCheckout --> PassarellaPagament
    FacanaCheckout --> RepositoriComandes
    FacanaCheckout --> ServeiNotificacions
Rol GoF A PideYa
Facade FacanaCheckout
Subsystem classes (no coneixen la façana) ValidadorCistella, CalculadoraImpostos, PassarellaPagament, RepositoriComandes, ServeiNotificacions
Client Apps mòbils, web, atenció telefònica

La geometria explica la història: abans, un graf de 4 clients × 5 serveis; després, un embut. Els clients depenen d'una classe; el subsistema no sap que la façana existeix (fletxes només d'anada). El recompte de dependències cau de 20 a 4 + 5, i sobretot, el flux de negoci passa a estar escrit una vegada.

Implementació Java: FacanaCheckout

Al mòdul 2 va aparèixer un embrió d'aquesta idea: aquell ServeiCheckout que cobrava amb la família del mercat. La façana el completa amb el flux sencer, i rep les seves peces injectades —les dependents del mercat, acabades de sortir de la FabricaMercat—:

public class FacanaCheckout {

    private final ValidadorCistella validador;
    private final CalculadoraImpostos impostos;
    private final PassarellaPagament passarella;
    private final RepositoriComandes repositori;
    private final ServeiNotificacions notificacions;

    public FacanaCheckout(ValidadorCistella validador,
                          FabricaMercat fabricaMercat,
                          RepositoriComandes repositori,
                          ServeiNotificacions notificacions) {
        this.validador = validador;
        this.impostos = fabricaMercat.crearCalculadoraImpostos();  // familia coherent
        this.passarella = fabricaMercat.crearPassarellaPagament(); // del modul 2
        this.repositori = repositori;
        this.notificacions = notificacions;
    }

    /** L'operacio d'alt nivell: tot el flux de confirmacio, una sola crida. */
    public ConfirmacioComanda confirmarComanda(Cistella cistella, DadesPagament dadesPagament) {

        // 1. Validar: disponibilitat, quantia minima, zona de repartiment.
        validador.validar(cistella);                   // llanca CistellaInvalidaException

        // 2. Impostos del mercat correcte.
        BigDecimal base = cistella.getTotalSenseImpostos();
        BigDecimal impost = impostos.calcular(base);
        BigDecimal total = base.add(impost);

        // 3. Cobrar. Si falla, aqui s'acaba tot: encara no hi ha comanda per desfer.
        ResultatPagament resultat = passarella.cobrar(total, dadesPagament);
        if (!resultat.esAcceptat()) {
            throw new PagamentRebutjatException(resultat.getMotiu());
        }

        // 4. Crear la comanda (Builder del modul 2) i persistir-la.
        Comanda comanda = Comanda.builder(cistella.getClient(), cistella.getRestaurant())
                .linies(cistella.getLinies())
                .quantiaTotal(total)
                .referenciaPagament(resultat.getIdTransaccio())
                .build();
        repositori.desar(comanda);

        // 5. Notificar a client i restaurant.
        notificacions.notificarConfirmacio(comanda);

        return new ConfirmacioComanda(comanda.getNumero(), total, comanda.getHoraEstimada());
    }
}

I els quatre clients queden reduïts a la seva feina — interfície d'usuari:

// A l'app mobil, la web o on sigui:
ConfirmacioComanda conf = facanaCheckout.confirmarComanda(cistella, dadesPagament);
mostrarPantallaExit(conf);

Detalls que fan bona aquesta façana:

  • L'ordre amb intenció: cobrar abans de persistir fa innecessari el "desfer comanda" si el pagament falla. La decisió més delicada del flux està presa una vegada, al lloc correcte, i comentada.
  • Parla l'idioma del client: rep Cistella i DadesPagament, retorna una ConfirmacioComanda compacta (número, total, hora estimada) — no exposa ResultatPagament, ni la Comanda sencera, ni tipus interns del subsistema. Una façana que filtra tipus interns cap enfora és un túnel, no una porta.
  • Compon el que ja tenim construït: la coherència per mercat la continua garantint la FabricaMercat; el muntatge de la comanda, el seu Builder; l'enviament, el servei de notificacions (amb els seus decoradors de reintents, invisibles des d'aquí). La façana no reimplementa res: coordina.

Què NO és una façana

El patró es malinterpreta sovint per excés. Tres delimitacions importants:

No prohibeix l'accés directe al subsistema. El GoF és explícit: els clients que necessitin la potència completa poden continuar usant les classes del subsistema. El panell d'administració de PideYa consulta RepositoriComandes directament per als seus llistats, i és correcte: la façana ofereix una drecera per als casos comuns, no un peatge obligatori. (Una altra cosa és que un equip decideixi a més fer-la punt d'entrada únic d'un mòdul — decisió d'arquitectura legítima, però addicional al patró.)

No afegeix estat ni lògica de negoci pròpia. La façana coordina; les regles viuen al subsistema. Si FacanaCheckout comencés a calcular descomptes o a desar comptadors de comandes, estaria deixant de ser façana per convertir-se en un servei més — amb l'agreujant que la seva posició central la converteix en imant de responsabilitats: el camí directe cap al God Object que catalogarem entre els antipatrons.

No és "qualsevol classe que crida altres". La marca del patró és l'asimetria de complexitat: interfície d'alt nivell simple al davant, subsistema complex al darrere, i clients que gràcies a ella no coneixen el subsistema. Si la "façana" té un mètode per cada mètode intern, és un passamans que afegeix indirecció sense restar complexitat.

Façanes per capes i en APIs públiques

La idea escala més enllà d'una classe solta:

  • Façanes per capes: en arquitectures en capes, cada subsistema gran exposa la seva façana i amaga les seves tripes: FacanaCuina (recepció i estats de comandes), FacanaRepartiment (assignació de repartidors, seguiment), FacanaCheckout. Les capes superiors parlen només amb façanes; els equips poden reorganitzar l'interior del seu subsistema sense trencar ningú. És el patró fent de frontera de mòdul — embrió del que, a una altra escala, faran els serveis a les arquitectures modernes (només menció).
  • Façanes en APIs públiques: quan PideYa publiqui la seva API per a partners ("crear comanda", "consultar estat"), aquella API serà una façana institucional: operacions d'alt nivell, tipus propis de la frontera, i ni rastre dels vint serveis interns. Les llibreries ho fan igual: SLF4J ("Simple Logging Facade for Java") és una façana confessa sobre els subsistemes de logging; javax.xml.parsers ho és sobre la maquinària de parseig XML.
  • Façana + Adapter, el duo de frontera: de portes enfora, una façana simplifica el que és nostre per a altres; un adaptador tradueix l'aliè per a nosaltres. A la frontera de tot sistema sa solen trobar-se tots dos, cadascun mirant a un costat.

Quan usar-lo i quan no

Usa'l quan:

  • Diversos clients repeteixen la mateixa coordinació d'un subsistema (el símptoma del checkout: flux copiat amb divergències).
  • Vols estratificar: definir punts d'entrada per capa o mòdul i reduir l'acoblament entre subsistemes.
  • Un flux de negoci delicat (ordre, transaccionalitat, errors) mereix viure escrit una sola vegada i amb tests propis.
  • Publiques una API —interna entre equips o externa a partners— i necessites una superfície estable que desacobli els consumidors de la teva evolució interna.

No l'usis quan:

  • Hi ha un sol client i una sola crida simple: embolcallar dues crides en una classe nova és indirecció sense renda (KISS).
  • L'usaries per amagar un mal disseny: si el subsistema és un nus incomprensible, la façana només hi posa una cortina al davant; el nus continua creixent al darrere. Primer ordena (potser amb els altres patrons del mòdul), després, si encara aporta, façana.
  • Cada client necessita un flux diferent: una façana amb quinze variants de confirmarComanda(...) sobrecarregades és la duplicació original, ara centralitzada i amb paràmetres booleans.

Relació amb altres patrons (només menció): Adapter tradueix una interfície, Facade en simplifica moltes — l'acarament fi, a la comparativa; la façana sol crear-se amb les fàbriques del mòdul 2 i ser instància única compartida (Singleton o, millor, injecció); Mediator també centralitza interaccions, però entre col·legues que es comuniquen entre si i amb trànsit d'anada i tornada — el distingirem al mòdul 4; i dins del subsistema, la façana conviu amb tot el que hem vist sense fricció.

Errors Comuns i Consells

  • La façana que s'engreixa. Cada sprint algú afegeix "un metodet més" i al cap de dos anys FacanaCheckout té 40 mètodes i lògica pròpia. Vigila la bàscula: si creixen els casos d'ús, crea façanes per àrea (checkout, seguiment, valoracions), no una façana universal.
  • Filtrar tipus interns. Retornar ResultatPagament o entitats JPA del subsistema a través de la façana torna a acoblar els clients amb el que volíem amagar. La frontera defineix els seus propis tipus (ConfirmacioComanda), igual que exigim als adaptadors.
  • Façana obligatòria per dogma. Prohibir tot accés directe al subsistema "perquè hi ha façana" obliga a duplicar-hi operacions legítimes de baix nivell. Recorda: el patró simplifica el cas comú; no jura exclusivitat.
  • Lògica de negoci de contraban. El if (resultat.esAcceptat()) és coordinació (decidir el flux); un if (client.esVip()) descompte = ... seria negoci (decidir regles) i pertany al subsistema. La línia és fina: pregunta't si la regla tindria sentit també invocada des d'un altre flux — si sí, cap avall amb ella.
  • No testejar la façana com a flux. La façana és el lloc natural dels tests d'orquestració: amb dobles dels cinc serveis, verifica l'ordre, el tall quan el pagament falla, la no-notificació en error. Si aquests tests no existeixen, el flux més delicat del negoci està sense xarxa.
  • Consell: dissenya la interfície de la façana des del client, no des del subsistema: primer escriu com vols que es llegeixi el codi de l'app (confirmarComanda(cistella, pagament)), i després fes que la façana ho compleixi. Les façanes dissenyades des de dins acaben fent olor de les tripes que havien d'ocultar.

Exercicis

Exercici 1: la façana de seguiment

La pantalla "on és la meva comanda?" necessita: estat de la comanda (ServeiCuina.estatDe(numeroComanda)), posició i nom del repartidor si ja ha sortit (ServeiRepartiment.repartimentDe(numeroComanda)), i hora estimada recalculada (CalculadoraETA.estima(comanda, repartiment)). Avui les tres crides i la seva combinació estan copiades a iOS, Android i web. Dissenya FacanaSeguiment: firma pública, tipus de retorn propi de la frontera i esquelet d'implementació.

Exercici 2: la façana impostora

Quines dues violacions del patró conté aquesta versió i per què són perilloses?

public class FacanaCheckout {
    private int comandesConfirmadesAvui = 0;                    // (a)

    public ConfirmacioComanda confirmarComanda(Cistella cistella, DadesPagament dades) {
        if (cistella.getClient().esVip() && comandesConfirmadesAvui < 100) {
            cistella.aplicarDescompte(new BigDecimal("0.05")); // (b)
        }
        // ... resta del flux com abans ...
        comandesConfirmadesAvui++;
        // ...
    }
}

Exercici 3: accés directe o ampliar la façana?

L'equip de comptabilitat necessita, per al seu tancament diari, la llista de transaccions de pagament del dia tal com les retorna la passarel·la. Discuteix: han de demanar-la a través de FacanaCheckout o anar directes al subsistema de pagaments? Dóna un criteri general reutilitzable.

Solucions

Solució 1:

public class FacanaSeguiment {

    private final ServeiCuina cuina;
    private final ServeiRepartiment repartiment;
    private final CalculadoraETA eta;

    public FacanaSeguiment(ServeiCuina cuina, ServeiRepartiment repartiment, CalculadoraETA eta) {
        this.cuina = cuina; this.repartiment = repartiment; this.eta = eta;
    }

    /** Tipus propi de la frontera: nomes el que la pantalla necessita. */
    public record EstatComandaClient(String fase, String nomRepartidor,
                                     Posicio posicioRepartidor, LocalTime horaEstimada) {}

    public EstatComandaClient estatDe(NumeroComanda numero) {
        EstatComanda comanda = cuina.estatDe(numero);
        Optional<Repartiment> enCurs = repartiment.repartimentDe(numero);
        LocalTime estimada = eta.estima(comanda, enCurs.orElse(null));
        return new EstatComandaClient(
                comanda.faseLlegible(),
                enCurs.map(Repartiment::getNomRepartidor).orElse(null),
                enCurs.map(Repartiment::getPosicio).orElse(null),
                estimada);
    }
}

Claus: una sola crida per als tres clients, retorn propi (EstatComandaClient, un record de frontera) en lloc de filtrar EstatComanda/Repartiment, i la combinació (què passa si encara no hi ha repartidor) escrita una vegada.

Solució 2: (a) Estat propi: comandesConfirmadesAvui converteix la façana en propietària d'una dada de negoci que ningú més no veu — es perd en reiniciar, falla amb diverses instàncies i duplica una veritat que pertany al repositori. (b) Lògica de negoci de contraban: la regla del descompte VIP viu on ningú no la buscarà (la trobarà l'equip de promocions quan canviï la política?) i és invisible per a qualsevol altre flux que confirmi comandes (l'API de partners no l'aplicaria: preus inconsistents). Totes dues coses a més fan la façana difícil de substituir o testejar, que era la seva gràcia: ha de tornar a ser coordinació pura i les regles, baixar al subsistema (servei de promocions, repositori).

Solució 3: directes al subsistema de pagaments (o millor, a una façana de l'àrea de pagaments si existeix). Criteri reutilitzable: la façana serveix els casos d'ús comuns dels seus clients, al seu nivell d'abstracció; comptabilitat demana dades crues d'un subsistema concret, en el vocabulari d'aquell subsistema, per a un cas d'ús aliè al checkout. Ampliar FacanaCheckout amb transaccionsDelDia() la desviaria del seu propòsit i l'engreixaria (error "façana que s'engreixa"). El patró no prohibeix l'accés directe: usa'l quan el cas d'ús no és de la façana.

Conclusió

Facade posa una porta simple davant d'un subsistema complex: una classe que ofereix les operacions d'alt nivell que els clients realment necessiten, escriu el flux delicat una sola vegada i redueix l'acoblament d'un graf a un embut. Saps també el que no és: ni prohibició d'accés directe, ni magatzem d'estat, ni penjador per a lògica de negoci. A PideYa, FacanaCheckout va reunir validació, impostos per mercat, cobrament, construcció de la comanda i notificació — tot reutilitzant peces d'aquest mòdul i de l'anterior, que és el millor senyal que el sistema està ben compost.

Canviem ara de preocupació per complet: de l'ordre a l'escala. El mapa en temps real de PideYa dibuixa milers de repartidors i restaurants, i cada marcador arrossega la seva icona, els seus bytes, la seva memòria... multiplicats per deu mil. Quan els objectes són multitud, la pregunta estructural és una altra: quant del que cada objecte carrega és realment seu, i quant podria compartir-se entre tots? Ens veiem a Flyweight.

Curs de Patrons de Disseny de Programari

Mòdul 1: Introducció als Patrons de Disseny

Mòdul 2: Patrons Creacionals

Mòdul 3: Patrons Estructurals

Mòdul 4: Patrons de Comportament

Mòdul 5: Aplicació de Patrons de Disseny

Mòdul 6: Patrons de Disseny Avançats

Mòdul 7: Recursos Addicionals i Conclusió

© Copyright 2026. Tots els drets reservats