Por que CQRS e Event Sourcing não são apenas palavras da moda
\n\nImagine que seu código é um enorme museu. Normalmente, armazenamos apenas a versão mais recente do artefato: "é assim que o pedido se parece agora". Mas e se quisermos saber como o pedido chegou a esse estado? Quem apertou o botão "Enviar"? Quando o status mudou? É aqui que entram em cena CQRS (Command Query Responsibility Segregation) e Event Sourcing em Kotlin.
\n\nNeste guia, não vamos apenas copiar a teoria da Wikipedia. Vamos analisar exemplos reais, escrever código e entender como esses padrões transformam o caos das operações CRUD em uma arquitetura enxuta. Se você está cansado do "código espaguete" e quer construir sistemas fáceis de escalar e depurar — está no lugar certo.
\n\nO que é CQRS em palavras simples
\n\nNa arquitetura padrão, usamos os mesmos modelos para ler e escrever dados. É como se você estivesse comendo sopa e ao mesmo tempo mexendo com a mesma colher. O CQRS propõe separar as responsabilidades:
\n\n- \n
- Command (Comando) — altera o estado. Requisições de escrita (Create, Update, Delete). \n
- Query (Consulta) — lê o estado. Apenas operações SELECT. \n
Por que isso é necessário? Imagine uma loja online. Quando um usuário finaliza um pedido, precisamos verificar o estoque, dar baixa no produto, criar um registro no banco de dados, enviar um e-mail. Isso é um comando complexo. E quando o cliente apenas visualiza a lista de seus pedidos — isso é uma consulta simples. Misturá-los em um único método é o caminho certo para os bugs.
\n\nVantagens do CQRS em Kotlin
\n- \n
- Desempenho: é possível otimizar leitura e escrita de formas diferentes (bancos de dados diferentes, caches, índices). \n
- Segurança: separação de direitos de acesso (admin escreve, usuário lê). \n
- Escalabilidade: comandos e consultas podem ser distribuídos em servidores diferentes. \n
Event Sourcing: preservando a história, não os fatos
\n\nSe o CQRS separa as operações, o Event Sourcing muda a forma de armazenar dados. Em vez de armazenar o estado atual (por exemplo, "saldo: 1000 reais"), armazenamos uma sequência de eventos: "Depósito de 500", "Compra de 200", "Dedução de taxa de 50".
\n\nO estado atual é simplesmente o resultado da aplicação de todos os eventos em ordem. Isso dá um superpoder: podemos "rebobinar" o tempo, restaurar o estado em qualquer data ou analisar o comportamento dos usuários.
\n\nExemplo da vida real: conta bancária
\n// Evento\ndata class MoneyDeposited(val amount: BigDecimal, val timestamp: Instant)\ndata class MoneyWithdrawn(val amount: BigDecimal, val timestamp: Instant)\n\n// Agregado (entidade)\nclass BankAccount {\n private var balance: BigDecimal = BigDecimal.ZERO\n private val events = mutableListOf<Any>()\n\n fun deposit(amount: BigDecimal) {\n if (amount <= BigDecimal.ZERO) throw IllegalArgumentException("O valor deve ser positivo")\n val event = MoneyDeposited(amount, Instant.now())\n apply(event)\n events.add(event)\n }\n\n fun withdraw(amount: BigDecimal) {\n if (amount > balance) throw IllegalStateException("Saldo insuficiente")\n val event = MoneyWithdrawn(amount, Instant.now())\n apply(event)\n events.add(event)\n }\n\n private fun apply(event: Any) {\n when (event) {\n is MoneyDeposited -> balance += event.amount\n is MoneyWithdrawn -> balance -= event.amount\n }\n }\n\n fun getBalance(): BigDecimal = balance\n fun getUncommittedEvents(): List<Any> = events.toList()\n}\n\nComo CQRS e Event Sourcing trabalham juntos
\n\nEsta é a dupla clássica. O CQRS é responsável por separar os canais, e o Event Sourcing é responsável pelo armazenamento