Facade este un pattern structural de proiectare care oferă o interfață simplificată către un subsistem complex de clase. În dezvoltarea mobilă, Facade este cel mai des implementat ca Service Layer sau UseCase, ascunzând interacțiunea cu rețeaua, baza de date și analitica. Conform lui Martin Fowler (Patterns of Enterprise Application Architecture, 2003), Facade este unul dintre patternurile-cheie pentru organizarea stratului de servicii.
Idei principale
Facade — pattern structural care oferă o interfață unificată către un grup de interfețe ale subsistemului. Definește o interfață de nivel înalt care simplifică utilizarea subsistemului. Facade nu adaugă funcționalitate nouă — orchesterază componentele existente, ascunzând de client complexitatea interacțiunii lor.
// Subsistem complex
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 — interfață simplă pentru 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 care ascunde AuthApi, UserDao și AnalyticsTracker de ViewModel. UI apelează loginUser(email, password) în loc de trei cereri separate către API, baza de date și analitică. Acest lucru reduce cuplajul: dacă mâine AuthApi devine FirebaseAuth sau UserDao migrează la Room, se schimbă doar Facade, nu și UI.
Service Layer — o implementare răspândită a Facade în aplicațiile mobile. Încapsulează logica de business și coordonarea între straturi. În Android, Service Layer este adesea implementat prin UseCase (Clean Architecture), în iOS — prin Manager sau protocoale Service.
| Component | Rol în subsistem | Ce ascunde Facade |
|---|---|---|
| AuthApi | Cerere de rețea către server | Formatul cererii, endpoint, gestionarea erorilor HTTP |
| UserDao | Stocarea locală a tokenului | Schema bazei de date, interogările SQL, migrările |
| AnalyticsTracker | Trimiterea evenimentelor de analitică | SDK Firebase/AppMetrica, formatul evenimentelor |
| NetworkMonitor | Verificarea disponibilității rețelei | ConnectivityManager, BroadcastReceiver |
AuthService unește toate cele patru componente. ViewModel apelează o singură metodă, fără să știe că sub capotă au loc o cerere de rețea, o scriere în baza de date, un tracking și o verificare a rețelei. La testare, AuthService poate fi înlocuit cu un mock, verificând toată logica de autentificare fără integrare cu componentele reale.
Facade, Adapter și Mediator — patternuri structurale, dar rezolvă probleme diferite. Sunt adesea confundate, deoarece toate trei introduc un obiect intermediar. Să analizăm diferențele pe exemplul unei aplicații mobile.
| Aspect | Facade | Adapter | Mediator |
|---|---|---|---|
| Scop | Simplificarea interfeței subsistemului | Transformarea interfeței | Reducerea cuplajului componentelor |
| Direcție | O interfață → subsistem | Client → Adaptee | N componente ↔ Mediator |
| Modificarea interfeței | Creează una nouă, simplificată | Transformă pe cea existentă | Nu schimbă, coordonează |
| Știe subsistemul despre pattern? | Nu | Nu | Da, comunică prin Mediator |
| Exemplu în dezvoltarea mobilă | UseCase / Service Layer | RecyclerView.Adapter | Coordinator în iOS |
Facade nu ascunde subsistemul — clientul poate apela AuthApi direct, dacă este necesar. Adapter modifică obligatoriu interfața Adaptee. Mediator coordonează interacțiunile complexe dintre multe obiecte care s-ar putea să nu se cunoască între ele.
Implementarea Facade în Kotlin pentru Android cu Clean Architecture folosește UseCase ca punct de intrare pentru fiecare scenariu de business. UseCase este un Facade care ascunde repository, mapper-ul și alte dependențe de stratul UI.
// Repository — este tot Facade, dar cu un nivel mai jos
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 pentru scenariul de 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 — Facade pentru scenariul de încărcare a profilului. Ascunde logica de cache (local → remote), maparea DTO → Entity → Profile și tracking-ul de analitică. ViewModel apelează invoke(userId) și primește un UserProfile gata sau o eroare. UseCase poate fi testat izolat, înlocuind repository cu un obiect mock.
Facade în iOS este adesea implementat ca Manager sau Service. Spre deosebire de Android, iOS folosește protocoale pentru definirea interfeței Facade, ceea ce permite înlocuirea ușoară a implementărilor în teste. Să analizăm un Facade pentru lucrul cu media — încărcare, cache și afișare.
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. Verifică cache-ul
if let cached = cache.image(for: url) {
return .success(cached)
}
// 2. Încarcă datele
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// 3. Decodează
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// 4. Salvează în cache
cache.setImage(image, for: url)
return .success(image)
}
}MediaService încapsulează un proces în trei pași: cache → încărcare → decodare. UI apelează o singură metodă loadImage(from:) în loc să gestioneze ImageCache, URLSession și ImageDecoder. La testare se poate înlocui MediaServiceProtocol cu un mock care returnează imagini prestabilite fără încărcare reală.
Greșelile la proiectarea Facade anulează avantajele lui: în loc de simplificare apare un God Object de care depinde tot sistemul. Să analizăm trei probleme principale.
Când un singur Facade conține metode pentru autentificare, încărcarea profilului, trimiterea mesajelor și sincronizare — acesta este un God Object. Semn: 15+ metode publice într-o singură clasă. Soluție: împărțirea în mai multe Facade specializate pe domenii de responsabilitate — AuthService, ProfileService, MessagingService.
Dacă Facade returnează tipuri specifice subsistemului (de exemplu, FirebaseUser sau RealmObject), clientul rămâne totuși legat de o implementare concretă. Soluție: Facade trebuie să returneze doar tipuri proprii (data class / struct), abstractizând complet clientul de detaliile subsistemului.
Când Facade interzice accesul direct la subsistem, devine un blocaj. Uneori clientul are nevoie de o metodă specifică a subsistemului, iar forțarea lui să treacă prin Facade este redundantă. Facade nu trebuie să fie un gatekeeper strict: oferă o interfață comodă, dar nu blochează accesul direct la componente.
Întrebări frecvente
Facade oferă o interfață simplificată către subsistem, creând adesea un set nou de metode. Proxy păstrează aceeași interfață ca obiectul original, dar adaugă controlul accesului sau încărcarea lazy. Facade — pentru simplificare, Proxy — pentru control.
Service Layer — este implementarea patternului Facade la nivelul arhitecturii aplicației. Definește granița dintre UI și logica de business, ascunzând detaliile implementării serviciilor. În Android, Service Layer este adesea implementat prin UseCase, în iOS — prin Manager sau protocoale Service.
God Facade apare când o singură clasă își asumă responsabilitatea pentru mai multe subsisteme fără legătură între ele. Semne: 15+ metode publice, metode din domenii diferite (autentificare + plăți + notificări), o clasă greu de testat (10+ dependențe). Soluție: împărțirea în Facade pe domenii.
Într-o aplicație cu 1-2 ecrane, Facade este redundant — apelul direct al API și al bazei de date din UI este mai simplu și mai clar. Facade se justifică la 5+ ecrane și 3+ subsisteme. În proiectele medii și mici este suficient Repository ca singur strat Facade, fără înveliș suplimentar de UseCase.
Facade simplifică testarea, deoarece înlocuiește întregul subsistem cu un singur obiect mock. În loc de mock pentru trei componente (rețea + bază de date + analitică) este suficient mock pentru un singur Facade. În Swift pentru aceasta se folosește protocol, în Kotlin — interface. Facade este comod și pentru testele de integrare, unde se verifică orchestrarea componentelor.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și