Facade, sau “fațada” e unul din cele mai comune design patterns, pentru că e folosit de o multitudine de biblioteci, indiferent care este platforma de dezvoltare sau limbajul de programare despre care vorbim.
Problema
Să zicem că avem următoarea situație: trebuie să scrii o bibliotecă, care să permită interacțiunea cu un serviciu sau componentă de infrastructură de cloud. O bază de date non-relațională, să spunem.
Vei lucra destul de intens în prima fază la funcționalitățile cheie, adică posibilitatea de a efectua acțiuni de citire, scriere, actualizare și ștergere pe baza de date respectivă.
Destul de simplu și la obiect până aici.
După aceste funcționalități de bază, realizezi că înainte de a permite interacțiunea cu baza de date trebuie să faci anumite verificări, sau pași intermediari, cum ar fi:
- Autentificarea utilizatorului
- Verificarea drepturilor pe tabela sau baza de date respectivă (acest proces poartă numele de autorizare)
- Determinarea celei mai apropiate regiuni pentru eficientizarea rutei
- Verificarea existenței bazei de date, etc.
Implementezi toți acești pași și începi să realizezi că utilizarea acestei biblioteci create de tine nu va fi deloc simplă, pentru că pașii trebuie executați într-o anumită ordine, altfel nu va funcționa.
Asta nu e o problemă neapărat, e normal ca anumite procese să depindă de o serie de pași secvențiali (adică unul după celălalt), dar asta va face utilizarea bibliotecii tale să fie greu de folosit și de înțeles.

class Authenticator:
getToken(username, password): string
// verificarea credențialelor și generarea tokenului de acces
class Authorizer:
authorize(token): boolean
// verificarea drepturilor de acces pe baza tokenului
class DatabaseConnector:
region: Region
DatabaseConnector(region: Region)
this.region = region
connect(): void
// realizarea conexiunii la baza de date
class RegionResolver:
identifyRegion(): Region
// identificarea celei mai apropiate regiuni pe baza locației clientului
class Application
authenticator: Authenticator
authorizer: Authorizer
dbConnector: DatabaseConnector
regionResolver: RegionResolver
main()
token = authenticator.getToken(username, password)
isAuthorized = authorizer.authorize(token)
if (!isAuthorized)
// .. eroare de autorizare
region = regionResolver.identifyRegion()
// obținerea conexiunii
dbConnector.connect()
// .. acțiuni de citire, scriere etc. în baza de date
Soluția: fațada
Aici vine “fațada” să ne salveze.
Fațada presupune adăugarea unei clase de “fațadă” care va orchestra tot procesul cu efort minim din partea clientului. (în terminologia tehnică, termenul “client” face referire la codul apelant).
Simplu spus, fațada doar agreghează și execută pașii pe care clientul ar trebui să-i facă pentru a-i ușura efortul.
După implementarea fațadei, componentele noastre de cod din diagrama anterioară ar trebui să arate așa:

Codul client folosește acum CloudDatabaseFacade pentru a comunica cu baza de date într-un mod mult mai simplificat față de cel anterior.
Sigur, exemplul este unul care omite multe alte detalii, pentru că în cele din urmă acea fațadă tot ar trebui să fie configurată cu toate elementele necesare, cum ar fi credențialele de acces, numele bazei de date șamd. pentru a funcționa, dar ideea principală este ca fațada să fie unicul punct de contact.
class Authenticator:
getToken(username, password): string
// verificarea credențialelor și generarea tokenului de acces
class Authorizer:
authorize(token): bool
// verificarea drepturilor de acces pe baza tokenului
class DatabaseConnector:
region: Region
DatabaseConnector(region: Region)
this.region = region
connect(): void
// realizarea conexiunii la baza de date
class RegionResolver:
identifyRegion(): Region
// identificarea celei mai apropiate regiuni pe baza locației clientului
class CloudDatabaseFacade:
authenticator: Authenticator
authorizer: Authorizer
dbConnector: DatabaseConnector
regionResolver: RegionResolver
connect(): void
token = authenticator.getToken(username, password)
isAuthorized = authorizer.authorize(token)
if (!isAuthorized)
// .. eroare de autorizare
region = regionResolver.identifyRegion()
// obținerea conexiunii
dbConnector.connect()
class Application
dbFacade: CloudDatabaseFacade
main()
dbFacade.connect()
// .. acțiuni de citire, scriere etc. în baza de date
Exemplul dat, deși a fost simplificat, e unul cât se poate de real.
Toți furnizorii de servicii de cloud oferă astfel de biblioteci pentru interacțiunea cu serviciile lor care în spate ridică tot greul pentru tine, astfel să te poți concentra pe logica aplicației tale, nu pe configurarea infrastructurii.
Un alt exemplu, la fel de real, e următorul: servicii de back-end, cum ar fi web APIs, pun la dispoziția celor care trebuie să le apeleze biblioteci prin intermediul cărora se pot integra cu acele API-uri.
De multe ori, aceste biblioteci expun o clasă de tip “client” care intermediază toate operațiunile necesare pentru apelarea unui anume API, cât și procesele intermediare, cum ar fi autentificarea, autorizarea etc. făcând astfel procesul de interacțiune dintre diferite servicii în back-end extrem de simplu.
Când să folosești fațada
Cel mai evident semn că trebuie să folosești o fațadă în implementarea ta e situația de care îți povesteam mai devreme, în care ai mai multe componente de cod care trebuie să fie apelate într-un anume fel și într-o anumită ordine pentru a obține un rezultat.
În exemplul dat, rezultatul era conexiunea la o bază de date din cloud făcută în 4 pași:
- Autentificarea și obținerea unui token
- Verificarea drepturilor acelui token cu respectiva bază de date
- Identificarea celei mai apropiate regiuni (furnizorii de servicii de cloud au mai multe regiuni pe glob, se folosește în general regiunea mai apropiată de locația clientului)
- Realizarea conexiunii cu baza de date
Complexitatea asta a fost ascunsă în spatele fațadei, care a preluat responsabilitatea de la client.
Atât pentru azi, ne auzim data viitoare.
