Facade: fondamenti del pattern fachada nell'architettura mobile

Autore: IT Sectr Pubblicato: 2026-02-18 Tempo di lettura: 9 min

Facade è un pattern di progettazione strutturale che fornisce un'interfaccia semplificata a un sottosistema complesso di classi. Nello sviluppo mobile, Facade è il più delle volte implementato come Service Layer o UseCase, nascondendo l'interazione con la rete, il database e l'analisi. Secondo Martin Fowler (Patterns of Enterprise Application Architecture, 2003), Facade è uno dei pattern chiave per organizzare il livello dei servizi.

L'essenziale

  • Facade — pattern strutturale che fornisce un'interfaccia semplice a un sistema complesso di classi, librerie o framework
  • Service Layer — implementazione di Facade nell'architettura mobile che nasconde API, cache e analisi alla UI
  • Facade non nasconde il sottosistema — il client può accedervi direttamente quando necessario
  • UseCase in Clean Architecture — una variante di Facade che orchestra un singolo scenario di business
  • Facade vs Adapter: Facade semplifica l'interfaccia, Adapter trasforma un'interfaccia in un'altra

Cos'è il pattern Facade?

Facade è un pattern strutturale che fornisce un'interfaccia unificata a un gruppo di interfacce del sottosistema. Definisce un'interfaccia di alto livello che semplifica l'uso del sottosistema. Facade non aggiunge nuove funzionalità — orchestra i componenti esistenti, nascondendo la complessità della loro interazione al client.

Kotlin
// Sottosistema complesso
class AuthApi {
    suspend fun login(email: String, pass: String): TokenResponse
}

class UserDao {
    suspend fun saveUser(user: UserEntity)
    suspend fun getUser(id: Long): UserEntity?
}

class AnalyticsTracker {
    fun track(event: String, params: Map)
}

// Facade — un'interfaccia semplice per la UI
class AuthService(
    private val api: AuthApi,
    private val dao: UserDao,
    private val analytics: AnalyticsTracker
) {
    suspend fun loginUser(email: String, password: String): Result {
        return runCatching {
            val token = api.login(email, password)
            val user = User(token.userId, email, token.accessToken)
            dao.saveUser(user.toEntity())
            analytics.track("login_success", mapOf("method" to "email"))
            user
        }
    }
}

AuthService è una Facade che nasconde AuthApi, UserDao e AnalyticsTracker al ViewModel. La UI chiama loginUser(email, password) invece di tre richieste separate all'API, al database e all'analisi. Questo riduce l'accoppiamento: se domani AuthApi diventa FirebaseAuth o UserDao migra su Room, cambia solo la Facade, non la UI.

Facade nell'architettura mobile: Service Layer

Service Layer è un'implementazione comune di Facade nelle applicazioni mobili. Incapsula la logica di business e il coordinamento tra i livelli. In Android, il Service Layer è spesso implementato tramite UseCase (Clean Architecture), e in iOS tramite i protocolli Manager o Service.

Componente Ruolo nel sottosistema Cosa nasconde Facade
AuthApi Richiesta di rete al server Formato della richiesta, endpoint, gestione degli errori HTTP
UserDao Archiviazione locale del token Schema del database, query SQL, migrazioni
AnalyticsTracker Invio di eventi di analisi SDK Firebase/AppMetrica, formato degli eventi
NetworkMonitor Verifica della disponibilità della rete ConnectivityManager, BroadcastReceiver

AuthService combina tutti e quattro i componenti. Il ViewModel chiama un unico metodo senza sapere che, sotto il cofano, avvengono una richiesta di rete, una scrittura nel database, un tracciamento e una verifica della rete. Durante i test, AuthService può essere sostituito con un mock, verificando l'intera logica di autenticazione senza integrazione con componenti reali.

Facade vs Adapter vs Mediator

Facade, Adapter e Mediator sono pattern strutturali, ma risolvono problemi diversi. Vengono spesso confusi, poiché tutti e tre introducono un oggetto intermediario. Analizziamo le differenze usando come esempio un'applicazione mobile.

Aspetto Facade Adapter Mediator
Obiettivo Semplificare l'interfaccia del sottosistema Trasformare un'interfaccia Ridurre l'accoppiamento tra i componenti
Direzione Un'interfaccia → sottosistema Client → Adaptee N componenti ↔ Mediator
Modifica dell'interfaccia Crea una nuova, semplificata Trasforma quella esistente Non la modifica, coordina
Il sottosistema conosce il pattern? No No Sì, comunica tramite il Mediator
Esempio nello sviluppo mobile UseCase / Service Layer RecyclerView.Adapter Coordinator in iOS

Facade non nasconde il sottosistema — il client può accedere ad AuthApi direttamente quando necessario. Adapter modifica obbligatoriamente l'interfaccia dell'Adaptee. Mediator coordina interazioni complesse tra molti oggetti che potrebbero non conoscersi tra loro.

Implementazione di Facade in Kotlin per Android

L'implementazione di Facade in Kotlin per Android con Clean Architecture usa UseCase come punto di ingresso per ogni scenario di business. UseCase è una Facade che nasconde repository, mapper e altre dipendenze al livello UI.

Kotlin
// Repository è anch'esso una Facade, ma a un livello inferiore
class UserRepositoryImpl(
    private val local: UserLocalDataSource,
    private val remote: UserRemoteDataSource,
    private val mapper: UserMapper
) : UserRepository {
    override suspend fun getUserProfile(id: String): UserProfile {
        val cached = local.getUser(id)
        if (cached != null && !cached.isStale) {
            return mapper.toProfile(cached)
        }
        val dto = remote.fetchUser(id)
        val entity = mapper.toEntity(dto)
        local.saveUser(entity)
        return mapper.toProfile(entity)
    }
}

// UseCase — una Facade per lo scenario di business
class LoadUserProfileUseCase(
    private val repo: UserRepository,
    private val analytics: AnalyticsTracker
) {
    suspend operator fun invoke(userId: String): Result {
        return runCatching {
            val profile = repo.getUserProfile(userId)
            analytics.track("profile_loaded", mapOf("user_id" to userId))
            profile
        }
    }
}

LoadUserProfileUseCase è una Facade per lo scenario di caricamento del profilo. Nasconde la logica di caching (local → remote), il mapping DTO → Entity → Profile e il tracciamento delle analisi. Il ViewModel chiama invoke(userId) e riceve un UserProfile pronto o un errore. UseCase può essere testato in isolamento, sostituendo il repository con un oggetto mock.

Implementazione di Facade in Swift per iOS

Facade in iOS è spesso implementato come Manager o Service. A differenza di Android, iOS usa i protocolli per definire l'interfaccia di Facade, il che consente di sostituire facilmente le implementazioni nei test. Consideriamo una Facade per lavorare con i media — caricamento, caching e visualizzazione.

Swift
protocol MediaServiceProtocol {
    func loadImage(from url: URL) async -> Result<UIImage, Error>
}

final class MediaService: MediaServiceProtocol {
    private let cache: ImageCache
    private let downloader: ImageDownloader
    private let decoder: ImageDecoder
    
    func loadImage(from url: URL) async -> Result<UIImage, Error> {
        // 1. Verificare la cache
        if let cached = cache.image(for: url) {
            return .success(cached)
        }
        // 2. Caricare i dati
        let result = await downloader.download(from: url)
        guard case let .success(data) = result else {
            return .failure(MediaError.downloadFailed)
        }
        // 3. Decodificare
        guard let image = decoder.decode(data) else {
            return .failure(MediaError.decodeFailed)
        }
        // 4. Salvare nella cache
        cache.setImage(image, for: url)
        return .success(image)
    }
}

MediaService incapsula un processo in tre fasi: cache → caricamento → decodifica. La UI chiama un unico metodo loadImage(from:) invece di gestire ImageCache, URLSession e ImageDecoder. Durante i test, MediaServiceProtocol può essere sostituito con un mock che restituisce immagini predefinite senza caricamento reale.

Errori tipici nell'uso di Facade

Gli errori nella progettazione di Facade annullano i suoi vantaggi: invece di semplificazione, si ottiene un God Object da cui dipende l'intero sistema. Analizziamo i tre problemi principali.

God Facade — troppa responsabilità

Quando un'unica Facade contiene metodi per l'autenticazione, il caricamento del profilo, l'invio di messaggi e la sincronizzazione — questo è un God Object. Segnale: più di 15 metodi pubblici in una classe. Soluzione: dividerla in più Facade specializzate per aree di responsabilità — AuthService, ProfileService, MessagingService.

Facade con perdita di dettagli del sottosistema

Se Facade restituisce tipi specifici del sottosistema (ad esempio, FirebaseUser o RealmObject), il client resta comunque legato a un'implementazione concreta. Soluzione: Facade deve restituire solo i propri tipi (data class / struct), astraendo completamente il client dai dettagli del sottosistema.

Facade come unico punto di ingresso

Quando Facade vieta l'accesso diretto al sottosistema, diventa un collo di bottiglia. A volte il client ha bisogno di un metodo specifico del sottosistema, e costringerlo a passare per Facade è ridondante. Facade non deve essere un gatekeeper rigido: fornisce un'interfaccia comoda, ma non blocca l'accesso diretto ai componenti.

Domande frequenti

Qual è la differenza tra Facade e Proxy?

Facade fornisce un'interfaccia semplificata al sottosistema, creando spesso un nuovo insieme di metodi. Proxy mantiene la stessa interfaccia dell'oggetto originale, ma aggiunge controllo degli accessi o caricamento lazy. Facade serve a semplificare, Proxy a controllare.

Facade è la stessa cosa di Service Layer?

Service Layer è un'implementazione del pattern Facade a livello di architettura dell'applicazione. Definisce il confine tra UI e logica di business, nascondendo i dettagli di implementazione dei servizi. In Android, il Service Layer è spesso implementato tramite UseCase; in iOS — tramite i protocolli Manager o Service.

Quando Facade diventa un God Object?

God Facade si verifica quando una classe si assume la responsabilità di più sottosistemi non correlati. Segnali: più di 15 metodi pubblici, metodi di domini diversi (autenticazione + pagamenti + notifiche), una classe difficile da testare (più di 10 dipendenze). Soluzione: dividere in Facade di dominio.

Facade è necessario in un'applicazione piccola?

In un'applicazione con 1-2 schermate, Facade è ridondante — chiamare API e database direttamente dalla UI è più semplice e chiaro. Facade ripaga con 5+ schermate e 3+ sottosistemi. Nei progetti piccoli e medi è sufficiente un Repository come unico livello Facade, senza ulteriore wrapper UseCase.

Come testare codice che usa Facade?

Facade semplifica i test, poiché sostituisce un intero sottosistema con un unico oggetto mock. Invece di mockare tre componenti (rete + database + analisi), basta mockare una Facade. In Swift si usano i protocolli per questo; in Kotlin — le interfacce. Facade è utile anche per i test di integrazione, dove si verifica l'orchestrazione dei componenti.

Riepilogo

  • Facade — pattern strutturale che fornisce un'interfaccia semplice a un sottosistema complesso
  • Service Layer e UseCase — implementazioni comuni di Facade nell'architettura mobile
  • Facade non nasconde il sottosistema: il client può accedere ai componenti direttamente quando necessario
  • Facade vs Adapter: Facade semplifica, Adapter trasforma; Facade vs Mediator: Facade è unidirezionale, Mediator è bidirezionale
  • God Facade — antipattern: più di 15 metodi in una classe segnalano una violazione del Single Responsibility
  • Protocollo/interfaccia per Facade è obbligatorio — è l'unico modo per testare il sottosistema con i mock
  • Raccomandazione: introdurre Facade con 5+ schermate e 3+ sottosistemi; per i progetti piccoli è sufficiente Repository

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche