Facade: podstawy wzorca fasada w architekturze mobilnej

Autor: IT Sectr Opublikowano: 2026-02-18 Czas czytania: 9 min

Facade — to strukturalny wzorzec projektowy, który zapewnia uproszczony interfejs do złożonego podsystemu klas. W rozwoju aplikacji mobilnych Facade najczęściej jest implementowany jako Service Layer lub UseCase, ukrywający współpracę z siecią, bazą danych i analityką. Według Martina Fowlera (Patterns of Enterprise Application Architecture, 2003), Facade jest jednym z kluczowych wzorców organizacji warstwy usług.

Najważniejsze

  • Facade — strukturalny wzorzec zapewniający prosty interfejs do złożonego systemu klas, bibliotek lub frameworków
  • Service Layer — implementacja Facade w architekturze mobilnej, ukrywająca API, pamięć podręczną i analitykę przed UI
  • Facade nie ukrywa podsystemu — klient może uzyskać do niego bezpośredni dostęp w razie potrzeby
  • UseCase w Clean Architecture — odmiana Facade orkiestrująca jeden scenariusz biznesowy
  • Facade vs Adapter: Facade upraszcza interfejs, Adapter przekształca jeden interfejs w inny

Co to jest wzorzec Facade?

Facade — strukturalny wzorzec, który zapewnia ujednolicony interfejs do grupy interfejsów podsystemu. Definiuje interfejs wysokiego poziomu, upraszczający korzystanie z podsystemu. Facade nie dodaje nowej funkcjonalności — orkiestruje istniejące komponenty, ukrywając przed klientem złożoność ich wzajemnych interakcji.

Kotlin
// Złożony podsystem
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 — prosty interfejs dla 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 — Facade ukrywający AuthApi, UserDao i AnalyticsTracker przed ViewModel. UI wywołuje loginUser(email, password) zamiast trzech osobnych żądań do API, bazy danych i analityki. Zmniejsza to sprzężenie: jeśli jutro AuthApi stanie się FirebaseAuth, a UserDao zmigruje do Room, zmieni się tylko Facade, a nie UI.

Facade w architekturze mobilnej: Service Layer

Service Layer — popularna implementacja Facade w aplikacjach mobilnych. Enkapsuluje logikę biznesową i koordynację między warstwami. W Androidzie Service Layer często jest implementowany przez UseCase (Clean Architecture), w iOS — przez Manager lub protokoły Service.

KomponentRola w podsystemieCo ukrywa Facade
AuthApiŻądanie sieciowe do serweraFormat żądania, endpoint, obsługę błędów HTTP
UserDaoLokalne przechowywanie tokenaSchemat bazy danych, zapytania SQL, migracje
AnalyticsTrackerWysyłanie zdarzeń analitycznychSDK Firebase/AppMetrica, format zdarzeń
NetworkMonitorSprawdzanie dostępności sieciConnectivityManager, BroadcastReceiver

AuthService łączy wszystkie cztery komponenty. ViewModel wywołuje jedną metodę, nie wiedząc, że pod spodem odbywa się żądanie sieciowe, zapis do bazy, śledzenie i sprawdzanie sieci. Podczas testów AuthService można zastąpić mockiem, weryfikując całą logikę autoryzacji bez integracji z prawdziwymi komponentami.

Facade vs Adapter vs Mediator

Facade, Adapter i Mediator — wzorce strukturalne, ale rozwiązują różne zadania. Często są mylone, ponieważ wszystkie trzy wprowadzają obiekt pośredniczący. Przeanalizujmy różnice na przykładzie aplikacji mobilnej.

AspektFacadeAdapterMediator
CelUprościć interfejs podsystemuPrzekształcić interfejsZmniejszyć sprzężenie komponentów
KierunekJeden interfejs → podsystemKlient → AdapteeN komponentów ↔ Mediator
Zmiana interfejsuTworzy nowy, uproszczonyPrzekształca istniejącyNie zmienia, koordynuje
Czy podsystem zna wzorzec?NieNieTak, komunikuje się przez Mediator
Przykład w rozwoju mobilnymUseCase / Service LayerRecyclerView.AdapterCoordinator w iOS

Facade nie ukrywa podsystemu — klient może w razie potrzeby uzyskać bezpośredni dostęp do AuthApi. Adapter obligatoryjnie zmienia interfejs Adaptee. Mediator koordynuje złożone interakcje między wieloma obiektami, które mogą się nie znać nawzajem.

Implementacja Facade w Kotlin dla Androida

Implementacja Facade w Kotlin dla Androida z Clean Architecture wykorzystuje UseCase jako punkt wejścia dla każdego scenariusza biznesowego. UseCase — to Facade ukrywający repozytorium, mapper i inne zależności przed warstwą UI.

Kotlin
// Repository — też Facade, ale o poziom niżej
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 — Facade dla scenariusza biznesowego
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 — Facade dla scenariusza ładowania profilu. Ukrywa logikę buforowania (local → remote), mapowanie DTO → Entity → Profile i śledzenie analityki. ViewModel wywołuje invoke(userId) i otrzymuje gotowy UserProfile lub błąd. UseCase można testować w izolacji, zastępując repozytorium obiektem mock.

Implementacja Facade w Swift dla iOS

Facade w iOS często jest implementowany jako Manager lub Service. W przeciwieństwie do Androida, iOS używa protokołów do definiowania interfejsu Facade, co ułatwia podmianę implementacji w testach. Rozważmy Facade do pracy z mediami — ładowanie, buforowanie i wyświetlanie.

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. Sprawdzić pamięć podręczną
        if let cached = cache.image(for: url) {
            return .success(cached)
        }
        // 2. Pobrać dane
        let result = await downloader.download(from: url)
        guard case let .success(data) = result else {
            return .failure(MediaError.downloadFailed)
        }
        // 3. Zdekodować
        guard let image = decoder.decode(data) else {
            return .failure(MediaError.decodeFailed)
        }
        // 4. Zapisać w pamięci podręcznej
        cache.setImage(image, for: url)
        return .success(image)
    }
}

MediaService enkapsuluje proces trzykrokowy: pamięć podręczna → ładowanie → dekodowanie. UI wywołuje jedną metodę loadImage(from:) zamiast zarządzania ImageCache, URLSession i ImageDecoder. Podczas testów można podmienić MediaServiceProtocol mockiem zwracającym wcześniej przygotowane obrazy bez prawdziwego ładowania.

Typowe błędy przy użyciu Facade

Błędy w projektowaniu Facade niwelują jego zalety: zamiast uproszczenia powstaje God Object, od którego zależy cały system. Przeanalizujmy trzy główne problemy.

God Facade — zbyt duża odpowiedzialność

Kiedy jeden Facade zawiera metody autoryzacji, ładowania profilu, wysyłania wiadomości i synchronizacji — to God Object. Oznaka: 15+ metod publicznych w jednej klasie. Rozwiązanie: podzielić na kilka wyspecjalizowanych Facade według obszarów odpowiedzialności — AuthService, ProfileService, MessagingService.

Facade z wyciekiem szczegółów podsystemu

Jeśli Facade zwraca typy specyficzne dla podsystemu (np. FirebaseUser lub RealmObject), klient i tak jest związany z konkretną implementacją. Rozwiązanie: Facade powinien zwracać tylko własne typy (data class / struct), całkowicie abstrahując klienta od szczegółów podsystemu.

Facade jako jedyne wejście

Kiedy Facade zabrania bezpośredniego dostępu do podsystemu, staje się wąskim gardłem. Czasami klient potrzebuje specyficznej metody podsystemu i zmuszanie go do przechodzenia przez Facade jest zbędne. Facade nie powinien być ścisłym gatekeeperem: zapewnia wygodny interfejs, ale nie blokuje bezpośredniego dostępu do komponentów.

Często zadawane pytania

Jaka jest różnica między Facade a Proxy?

Facade zapewnia uproszczony interfejs do podsystemu, często tworząc nowy zestaw metod. Proxy zachowuje ten sam interfejs, co oryginalny obiekt, ale dodaje kontrolę dostępu lub leniwe ładowanie. Facade — dla uproszczenia, Proxy — dla kontroli.

Czy Facade to to samo co Service Layer?

Service Layer — to implementacja wzorca Facade na poziomie architektury aplikacji. Definiuje granicę między UI a logiką biznesową, ukrywając szczegóły implementacji usług. W Androidzie Service Layer często jest implementowany przez UseCase, w iOS — przez Manager lub protokoły Service.

Kiedy Facade staje się God Object?

God Facade powstaje, gdy jedna klasa bierze odpowiedzialność za kilka niepowiązanych podsystemów. Oznaki: 15+ metod publicznych, metody z różnych domen (autoryzacja + płatności + powiadomienia), klasa trudna do testowania (10+ zależności). Rozwiązanie: podzielić na domenowe Facade.

Czy Facade jest potrzebny w małej aplikacji?

W aplikacji z 1-2 ekranami Facade jest zbędny — bezpośrednie wywołanie API i bazy danych z UI jest prostsze i bardziej zrozumiałe. Facade się opłaca przy 5+ ekranach i 3+ podsystemach. W średnich i małych projektach wystarczy Repository jako jedyna warstwa Facade, bez dodatkowej otoczki UseCase.

Jak testować kod korzystający z Facade?

Facade upraszcza testowanie, ponieważ zastępuje cały podsystem jednym obiektem mock. Zamiast mockowania trzech komponentów (sieć + baza + analityka) wystarczy mock jednego Facade. W Swift do tego używa się protocol, w Kotlin — interface. Facade jest też wygodny w testach integracyjnych, gdzie sprawdza się orkiestrację komponentów.

Podsumowanie

  • Facade — strukturalny wzorzec zapewniający prosty interfejs do złożonego podsystemu
  • Service Layer i UseCase — popularne implementacje Facade w architekturze mobilnej
  • Facade nie ukrywa podsystemu: klient może uzyskiwać bezpośredni dostęp do komponentów w razie potrzeby
  • Facade vs Adapter: Facade upraszcza, Adapter przekształca; Facade vs Mediator: Facade jest jednokierunkowy, Mediator dwukierunkowy
  • God Facade — antywzorzec: 15+ metod w jednej klasie sygnalizuje naruszenie Single Responsibility
  • Protokół/interfejs dla Facade jest obowiązkowy — to jedyny sposób na mock-testowanie podsystemu
  • Zalecenie: wprowadzaj Facade przy 5+ ekranach i 3+ podsystemach; dla małych projektów wystarczy Repository

Opracujemy aplikację mobilną pod klucz

IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.

Omów projekt

Przeczytaj również