Facade é um padrão de projeto estrutural que fornece uma interface simplificada para um subsistema complexo de classes. No desenvolvimento móvel, o Facade é mais frequentemente implementado como Service Layer ou UseCase, ocultando a interação com a rede, o banco de dados e a análise. Segundo Martin Fowler (Patterns of Enterprise Application Architecture, 2003), o Facade é um dos padrões-chave para organizar a camada de serviços.
O essencial
Facade é um padrão estrutural que fornece uma interface unificada para um grupo de interfaces do subsistema. Ele define uma interface de alto nível que simplifica o uso do subsistema. O Facade não adiciona nova funcionalidade — ele orquestra os componentes existentes, ocultando a complexidade da sua interação do cliente.
// Subsistema complexo
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 — uma interface simples para a 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 é um Facade que oculta AuthApi, UserDao e AnalyticsTracker do ViewModel. A UI chama loginUser(email, password) em vez de três requisições separadas à API, ao banco de dados e à análise. Isso reduz o acoplamento: se amanhã AuthApi virar FirebaseAuth ou UserDao migrar para Room, apenas o Facade muda, não a UI.
Service Layer é uma implementação comum do Facade em aplicativos móveis. Ele encapsula a lógica de negócio e a coordenação entre camadas. No Android, o Service Layer é frequentemente implementado via UseCase (Clean Architecture), e no iOS via protocolos Manager ou Service.
| Componente | Papel no subsistema | O que o Facade oculta |
|---|---|---|
| AuthApi | Requisição de rede ao servidor | Formato da requisição, endpoint, tratamento de erros HTTP |
| UserDao | Armazenamento local do token | Esquema do banco de dados, consultas SQL, migrações |
| AnalyticsTracker | Envio de eventos de análise | SDK Firebase/AppMetrica, formato de eventos |
| NetworkMonitor | Verificação da disponibilidade de rede | ConnectivityManager, BroadcastReceiver |
AuthService combina todos os quatro componentes. O ViewModel chama um único método sem saber que, por baixo dos panos, acontecem requisição de rede, escrita no banco, rastreamento e verificação de rede. Ao testar, o AuthService pode ser substituído por um mock, verificando toda a lógica de autenticação sem integração com componentes reais.
Facade, Adapter e Mediator são padrões estruturais, mas resolvem problemas diferentes. Eles são frequentemente confundidos, pois os três introduzem um objeto intermediário. Vamos analisar as diferenças usando um aplicativo móvel como exemplo.
| Aspecto | Facade | Adapter | Mediator |
|---|---|---|---|
| Objetivo | Simplificar a interface do subsistema | Transformar uma interface | Reduzir o acoplamento entre componentes |
| Direção | Uma interface → subsistema | Cliente → Adaptee | N componentes ↔ Mediator |
| Mudança de interface | Cria uma nova, simplificada | Transforma a existente | Não muda, coordena |
| O subsistema conhece o padrão? | Não | Não | Sim, comunica-se através do Mediator |
| Exemplo no desenvolvimento móvel | UseCase / Service Layer | RecyclerView.Adapter | Coordinator no iOS |
Facade não oculta o subsistema — o cliente pode acessar o AuthApi diretamente quando necessário. Adapter obrigatoriamente muda a interface do Adaptee. Mediator coordena interações complexas entre muitos objetos que podem não se conhecer.
A implementação do Facade em Kotlin para Android com Clean Architecture usa UseCase como ponto de entrada para cada cenário de negócio. UseCase é um Facade que oculta o repositório, o mapper e outras dependências da camada de UI.
// Repository também é um Facade, mas em nível inferior
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 — um Facade para o cenário de negócio
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 é um Facade para o cenário de carregamento de perfil. Ele oculta a lógica de cache (local → remoto), o mapeamento DTO → Entity → Profile e o rastreamento de análise. O ViewModel chama invoke(userId) e recebe um UserProfile pronto ou um erro. O UseCase pode ser testado isoladamente, substituindo o repositório por um objeto mock.
Facade no iOS é frequentemente implementado como Manager ou Service. Ao contrário do Android, o iOS usa protocolos para definir a interface do Facade, o que permite trocar facilmente as implementações nos testes. Vamos ver um Facade para trabalhar com mídia — carregamento, cache e exibição.
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. Verificar o cache
if let cached = cache.image(for: url) {
return .success(cached)
}
// 2. Carregar os dados
let result = await downloader.download(from: url)
guard case let .success(data) = result else {
return .failure(MediaError.downloadFailed)
}
// 3. Decodificar
guard let image = decoder.decode(data) else {
return .failure(MediaError.decodeFailed)
}
// 4. Salvar no cache
cache.setImage(image, for: url)
return .success(image)
}
}MediaService encapsula um processo de três etapas: cache → carregamento → decodificação. A UI chama um único método loadImage(from:) em vez de gerenciar ImageCache, URLSession e ImageDecoder. Ao testar, MediaServiceProtocol pode ser substituído por um mock que retorna imagens predefinidas sem carregamento real.
Os erros no projeto do Facade anulam suas vantagens: em vez de simplificação, você obtém um God Object do qual todo o sistema depende. Vamos analisar os três principais problemas.
Quando um único Facade contém métodos para autenticação, carregamento de perfil, envio de mensagens e sincronização — isso é um God Object. Sinal: mais de 15 métodos públicos em uma classe. Solução: dividir em vários Facades especializados por áreas de responsabilidade — AuthService, ProfileService, MessagingService.
Se o Facade retorna tipos específicos do subsistema (por exemplo, FirebaseUser ou RealmObject), o cliente ainda fica preso a uma implementação concreta. Solução: o Facade deve retornar apenas seus próprios tipos (data class / struct), abstraindo totalmente o cliente dos detalhes do subsistema.
Quando o Facade proíbe o acesso direto ao subsistema, ele se torna um gargalo. Às vezes o cliente precisa de um método específico do subsistema, e forçá-lo a passar pelo Facade é redundante. Facade não deve ser um gatekeeper rígido: ele fornece uma interface conveniente, mas não bloqueia o acesso direto aos componentes.
Perguntas frequentes
Facade fornece uma interface simplificada ao subsistema, frequentemente criando um novo conjunto de métodos. Proxy mantém a mesma interface do objeto original, mas adiciona controle de acesso ou carregamento preguiçoso. Facade é para simplificar, Proxy é para controlar.
Service Layer é uma implementação do padrão Facade no nível da arquitetura do aplicativo. Ele define a fronteira entre a UI e a lógica de negócio, ocultando os detalhes de implementação dos serviços. No Android, o Service Layer é frequentemente implementado via UseCase; no iOS, via protocolos Manager ou Service.
God Facade surge quando uma classe assume a responsabilidade por vários subsistemas não relacionados. Sinais: mais de 15 métodos públicos, métodos de domínios diferentes (autenticação + pagamentos + notificações), uma classe difícil de testar (mais de 10 dependências). Solução: dividir em Facades de domínio.
Em um aplicativo com 1-2 telas, o Facade é redundante — chamar a API e o banco de dados diretamente da UI é mais simples e claro. O Facade compensa com 5+ telas e 3+ subsistemas. Em projetos pequenos e médios, um Repository como única camada Facade é suficiente, sem envoltório adicional de UseCase.
Facade simplifica os testes, pois substitui todo um subsistema por um único objeto mock. Em vez de simular três componentes (rede + banco de dados + análise), basta simular um Facade. Em Swift, protocolos são usados para isso; em Kotlin, interfaces. O Facade também é conveniente para testes de integração que verificam a orquestração de componentes.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também