Como não se afogar no caos: padrões arquiteturais em Kotlin
Você já abriu seu projeto um mês depois de escrevê-lo e não conseguiu entender onde está a lógica de autenticação? Ou tentou adicionar um novo botão, e ele quebrou o funcionamento da lista? Familiar? Bem-vindo ao mundo dos aplicativos sem arquitetura.
Padrões arquiteturais em Kotlin não são uma moda passageira nem teorias abstratas. São a salvação do "código espaguete", onde tudo está misturado: lógica de negócios, UI, trabalho com rede e banco de dados. Neste artigo, vamos analisar quatro padrões principais: MVC, MVP, MVVM e MVI. Você os verá em ação com exemplos reais em Kotlin, entenderá as principais diferenças e poderá escolher a ferramenta certa para sua tarefa. Vamos lá!
O que é um padrão arquitetural e para que serve?
Um padrão arquitetural é o esqueleto do seu aplicativo. Ele dita como as classes se comunicam, onde armazenar dados e quem é responsável pela exibição. Sem ele, mais cedo ou mais tarde você terá um God Object — uma classe que faz tudo. Ou um Callback Hell — quando um callback gera outro, e o código se torna ilegível.
Kotlin, com sua sintaxe concisa e segurança contra nulos, é ideal para uma arquitetura limpa. Mas a linguagem é apenas metade do caminho. A outra metade é a escolha correta do padrão.
MVC (Model-View-Controller) — o clássico do gênero
O padrão mais antigo e simples. Sua essência:
- Model — dados e lógica de negócios.
- View — exibição (Activity, Fragment, Compose).
- Controller — elo de ligação, processa a entrada do usuário.
Em Kotlin (especialmente para Android), o MVC frequentemente se transforma em um "Controller Gordo". A Activity se torna View e Controller ao mesmo tempo. Exemplo:
// Ruim: Activity é View e Controller ao mesmo tempoclass LoginActivity : AppCompatActivity() { private lateinit var emailEditText: EditText private lateinit var passwordEditText: EditText
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login)
// Lógica do Controller diretamente aqui findViewById<Button>(R.id.loginButton).setOnClickListener { val email = emailEditText.text.toString() val password = passwordEditText.text.toString() // Validação e chamada de API — tudo no mesmo lugar if (email.isNotEmpty() && password.isNotEmpty()) { loginUser(email, password) } } }
private fun loginUser(email: String, password: String) { // Trabalho com rede — diretamente na Activity CoroutineScope(Dispatchers.IO).launch { val result = api.login(email, password) withContext(Dispatchers.Main) { // Atualização da UI — também aqui Toast.makeText(this@LoginActivity, result.message, Toast.LENGTH_SHORT).show() } } }}Vantagens: simplicidade, baixa barreira de entrada.
Desvantagens: View e Controller fortemente acoplados. Testar a lógica sem emulador Android é impossível. Para projetos grandes — um inferno.
MVP (Model-View-Presenter) — separação de poderes
O MVP resolve o principal problema do MVC: a View é separada da lógica através de interfaces. O Presenter é o "cérebro", que não sabe nada sobre Android. A View é uma camada passiva.
Exemplo em Kotlin:
// Interfaces para comunicaçãointerface LoginView { fun showLoading() fun hideLoading() fun showError(message: String) fun navigateToHome()}
class LoginPresenter(private val view: LoginView, private val authRepository: AuthRepository) {
fun login(email: String, password: String) { view.showLoading() CoroutineScope(Dispatchers.IO).launch {