Facade ist ein strukturelles Entwurfsmuster, das eine vereinfachte Schnittstelle zu einem komplexen Subsystem aus Klassen bereitstellt. In der mobilen Entwicklung wird Facade meist als Service Layer oder UseCase umgesetzt, der die Interaktion mit Netzwerk, Datenbank und Analytik verbirgt. Laut Martin Fowler (Patterns of Enterprise Application Architecture, 2003) ist Facade eines der wichtigsten Muster zur Organisation der Service-Schicht.
Das Wichtigste
Facade ist ein strukturelles Muster, das eine einheitliche Schnittstelle zu einer Gruppe von Subsystem-Schnittstellen bereitstellt. Es definiert eine High-Level-Schnittstelle, die die Nutzung des Subsystems vereinfacht. Facade fügt keine neue Funktionalität hinzu — es orchestriert vorhandene Komponenten und verbirgt die Komplexität ihrer Interaktion vor dem Client.
// Komplexes Subsystem
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 — eine einfache Schnittstelle für die 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 ist eine Facade, die AuthApi, UserDao und AnalyticsTracker vor dem ViewModel verbirgt. Die UI ruft loginUser(email, password) auf, statt drei separate Anfragen an API, Datenbank und Analytik zu senden. Das reduziert die Kopplung: Wenn AuthApi morgen zu FirebaseAuth wird oder UserDao auf Room migriert, ändert sich nur die Facade, nicht die UI.
Service Layer ist eine häufige Facade-Implementierung in mobilen Anwendungen. Er kapselt die Geschäftslogik und die Koordination zwischen den Schichten. In Android wird der Service Layer oft über UseCase (Clean Architecture) umgesetzt, in iOS über Manager- oder Service-Protokolle.
| Komponente | Rolle im Subsystem | Was die Facade verbirgt |
|---|---|---|
| AuthApi | Netzwerkanfrage an den Server | Anfrageformat, Endpoint, HTTP-Fehlerbehandlung |
| UserDao | Lokale Speicherung des Tokens | Datenbankschema, SQL-Abfragen, Migrationen |
| AnalyticsTracker | Senden von Analytik-Events | Firebase/AppMetrica SDK, Event-Format |
| NetworkMonitor | Prüfung der Netzwerkverfügbarkeit | ConnectivityManager, BroadcastReceiver |
AuthService vereint alle vier Komponenten. Das ViewModel ruft eine Methode auf, ohne zu wissen, dass im Hintergrund eine Netzwerkanfrage, ein Datenbankschreiben, Tracking und eine Netzwerkprüfung stattfinden. Beim Testen kann AuthService durch einen Mock ersetzt werden, um die gesamte Authentifizierungslogik ohne Integration mit echten Komponenten zu prüfen.
Facade, Adapter und Mediator sind strukturelle Muster, lösen aber unterschiedliche Aufgaben. Sie werden oft verwechselt, da alle drei ein Vermittler-Objekt einführen. Wir analysieren die Unterschiede am Beispiel einer mobilen Anwendung.
| Aspekt | Facade | Adapter | Mediator |
|---|---|---|---|
| Ziel | Subsystem-Schnittstelle vereinfachen | Schnittstelle umwandeln | Kopplung der Komponenten reduzieren |
| Richtung | Eine Schnittstelle → Subsystem | Client → Adaptee | N Komponenten ↔ Mediator |
| Schnittstellenänderung | Erstellt eine neue, vereinfachte | Wandelt die bestehende um | Ändert nicht, koordiniert |
| Kennt das Subsystem das Muster? | Nein | Nein | Ja, kommuniziert über den Mediator |
| Beispiel in der mobilen Entwicklung | UseCase / Service Layer | RecyclerView.Adapter | Coordinator in iOS |
Facade verbirgt das Subsystem nicht — der Client kann bei Bedarf direkt auf AuthApi zugreifen. Adapter ändert zwingend die Schnittstelle des Adaptee. Mediator koordiniert komplexe Interaktionen zwischen vielen Objekten, die sich gegenseitig nicht kennen müssen.
Die Facade-Implementierung in Kotlin für Android mit Clean Architecture nutzt UseCase als Einstiegspunkt für jedes Geschäftsszenario. UseCase ist eine Facade, die Repository, Mapper und andere Abhängigkeiten vor der UI-Schicht verbirgt.
// Repository ist auch eine Facade, aber auf niedrigerer Ebene
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 — eine Facade für das Geschäftsszenario
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 ist eine Facade für das Szenario der Profilladung. Es verbirgt die Caching-Logik (local → remote), das Mapping DTO → Entity → Profile und das Analytik-Tracking. Das ViewModel ruft invoke(userId) auf und erhält ein fertiges UserProfile oder einen Fehler. UseCase kann isoliert getestet werden, indem das Repository durch ein Mock-Objekt ersetzt wird.
Facade in iOS wird oft als Manager oder Service umgesetzt. Anders als Android nutzt iOS Protokolle zur Definition der Facade-Schnittstelle, was den Austausch von Implementierungen in Tests erleichtert. Wir betrachten eine Facade für die Arbeit mit Medien — Laden, Caching und Anzeige.
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. Cache prüfen
if let cached = cache.image(for: url) {
return .success(cached)
}
// 2. Daten laden
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// 3. Dekodieren
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// 4. Im Cache speichern
cache.setImage(image, for: url)
return .success(image)
}
}MediaService kapselt einen dreistufigen Prozess: Cache → Laden → Dekodieren. Die UI ruft eine Methode loadImage(from:) auf, statt ImageCache, URLSession und ImageDecoder zu verwalten. Beim Testen kann MediaServiceProtocol durch einen Mock ersetzt werden, der voreingestellte Bilder ohne echtes Laden zurückgibt.
Fehler beim Entwurf von Facade machen seine Vorteile zunichte: Statt Vereinfachung erhält man ein God Object, von dem das gesamte System abhängt. Wir analysieren die drei Hauptprobleme.
Wenn eine einzige Facade Methoden für Authentifizierung, Profilladung, Nachrichtensenden und Synchronisierung enthält — das ist ein God Object. Kennzeichen: mehr als 15 öffentliche Methoden in einer Klasse. Lösung: Aufteilen in mehrere spezialisierte Facades nach Verantwortungsbereichen — AuthService, ProfileService, MessagingService.
Wenn die Facade Typen zurückgibt, die für das Subsystem spezifisch sind (z. B. FirebaseUser oder RealmObject), bleibt der Client an eine konkrete Implementierung gebunden. Lösung: Die Facade sollte nur eigene Typen (data class / struct) zurückgeben und den Client vollständig von den Subsystem-Details abstrahieren.
Wenn die Facade den direkten Zugriff auf das Subsystem verbietet, wird sie zum Bottleneck. Manchmal braucht der Client eine spezifische Subsystem-Methode, und ihn durch die Facade zu zwingen, ist überflüssig. Facade sollte kein strikter Gatekeeper sein: Sie bietet eine bequeme Schnittstelle, blockiert aber keinen direkten Zugriff auf die Komponenten.
Häufig gestellte Fragen
Facade stellt eine vereinfachte Schnittstelle zum Subsystem bereit und erstellt oft einen neuen Satz von Methoden. Proxy behält dieselbe Schnittstelle wie das ursprüngliche Objekt bei, fügt aber Zugriffskontrolle oder Lazy Loading hinzu. Facade dient der Vereinfachung, Proxy der Kontrolle.
Service Layer ist eine Implementierung des Facade-Musters auf der Ebene der Anwendungsarchitektur. Er definiert die Grenze zwischen UI und Geschäftslogik und verbirgt die Implementierungsdetails der Dienste. In Android wird der Service Layer oft über UseCase umgesetzt, in iOS über Manager- oder Service-Protokolle.
God Facade entsteht, wenn eine Klasse die Verantwortung für mehrere unzusammenhängende Subsysteme übernimmt. Kennzeichen: mehr als 15 öffentliche Methoden, Methoden aus verschiedenen Domänen (Authentifizierung + Zahlungen + Benachrichtigungen), eine schwer testbare Klasse (mehr als 10 Abhängigkeiten). Lösung: Aufteilung in Domänen-Facades.
In einer Anwendung mit 1-2 Bildschirmen ist Facade überflüssig — der direkte Aufruf von API und Datenbank aus der UI ist einfacher und verständlicher. Facade zahlt sich aus bei 5+ Bildschirmen und 3+ Subsystemen. In kleinen und mittleren Projekten reicht ein Repository als einzige Facade-Schicht, ohne zusätzlichen UseCase-Wrapper.
Facade vereinfacht das Testen, da sie ein ganzes Subsystem durch ein einzelnes Mock-Objekt ersetzt. Statt drei Komponenten (Netzwerk + Datenbank + Analytik) zu mocken, reicht ein Mock der Facade. In Swift nutzt man dafür Protokolle, in Kotlin Interfaces. Facade ist auch für Integrationstests geeignet, bei denen die Orchestrierung der Komponenten geprüft wird.
Fazit
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch