Boas práticas e padrões · Arquitetura do código · 7 min de leitura
Clean Architecture e hexagonal (portas e adaptadores)
Regras de negócio no centro, frameworks na borda, dependências apontando sempre para dentro.
Arraste para o lado para ver o diagrama inteiro.
A regra da dependência
Martin (2012) unificou ideias da arquitetura hexagonal de Alistair Cockburn (2005) e da arquitetura cebola: o código só pode depender de camadas mais internas. Entidades e casos de uso não conhecem banco, HTTP nem UI. Por isso dá para testar o negócio sem nada disso.
Portas e adaptadores
Na hexagonal, o domínio define portas (interfaces) de entrada (o que o app oferece) e de saída (o que ele precisa: salvar, enviar). Adaptadores conectam o mundo real: controller HTTP, tela, Room, Retrofit.
Sem cerimônia
Para um app pequeno, três pacotes (ui, domain, data) bastam; casos de uso só valem quando há regra para colocar neles. O guia do Android marca a camada de domínio como opcional. A arquitetura deve acompanhar a complexidade do projeto.
No código
// domain (módulo puro Kotlin, sem Android)
data class Meta(val id: String, val valorAlvo: Long, val guardado: Long) { val progresso get() = guardado * 100 / valorAlvo }
interface MetasPort { suspend fun buscar(id: String): Meta?; suspend fun salvar(m: Meta) }
class Depositar(private val metas: MetasPort) {
suspend operator fun invoke(id: String, centavos: Long): Meta {
require(centavos > 0)
val m = metas.buscar(id) ?: error("meta não encontrada")
return m.copy(guardado = m.guardado + centavos).also { metas.salvar(it) }
}
}
// data (adaptador)
class RoomMetas(private val dao: MetaDao) : MetasPort { /* mapeia entity <-> domínio */ }
// ui
class MetaViewModel(private val depositar: Depositar) : ViewModel() O módulo domain compila sem Android e pode ir para o código compartilhado do KMP; Room é só um adaptador.
Em resumo
- Dependências apontam para o domínio.
- Domínio sem framework: testável e portável (KMP).
- Portas = interfaces; adaptadores = implementações.
- Comece simples; camadas a mais têm custo.
Vantagens e custos
- Regras de negócio testáveis e independentes de framework.
- Trocar banco, API ou UI afeta só a borda.
- Compartilhar o domínio em Kotlin Multiplatform.
- Mais interfaces, mapeamentos e arquivos.
- Cerimônia desnecessária em CRUD simples.