Por qué CQRS y Event Sourcing no son solo palabras de moda
\n\nImagine que su código es un enorme museo. Normalmente, solo guardamos la última versión de la pieza: "así es como se ve el pedido ahora". Pero, ¿y si queremos saber cómo llegó el pedido a ese estado? ¿Quién presionó el botón "Enviar"? ¿Cuándo cambió el estado? Aquí es donde entran en escena CQRS (Command Query Responsibility Segregation) y Event Sourcing en Kotlin.
\n\nEn esta guía no solo copiaremos la teoría de Wikipedia. Analizaremos ejemplos reales, escribiremos código y entenderemos cómo estos patrones transforman el caos de las operaciones CRUD en una arquitectura ordenada. Si está cansado del "código espagueti" y quiere construir sistemas que sean fáciles de escalar y depurar, está en el lugar correcto.
\n\nQué es CQRS en palabras sencillas
\n\nEn la arquitectura estándar, usamos los mismos modelos para leer y escribir datos. Es como si comiera sopa y la revolviera con la misma cuchara. CQRS propone separar la responsabilidad:
\n\n- \n
- Command (Comando) — cambia el estado. Solicitudes de escritura (Create, Update, Delete). \n
- Query (Consulta) — lee el estado. Solo operaciones SELECT. \n
¿Por qué es necesario? Imagine una tienda en línea. Cuando un usuario realiza un pedido, debemos verificar el inventario, descontar el producto, crear un registro en la base de datos, enviar un correo electrónico. Esto es un comando complejo. Pero cuando un cliente solo mira la lista de sus pedidos, es una consulta simple. Mezclarlos en un mismo método es el camino seguro hacia los errores.
\n\nVentajas de CQRS en Kotlin
\n- \n
- Rendimiento: se pueden optimizar la lectura y la escritura de manera diferente (diferentes bases de datos, cachés, índices). \n
- Seguridad: separación de permisos de acceso (el administrador escribe, el usuario lee). \n
- Escalabilidad: los comandos y las consultas se pueden distribuir en diferentes servidores. \n
Event Sourcing: guardamos la historia, no los hechos
\n\nSi CQRS separa las operaciones, Event Sourcing cambia la forma de almacenar los datos. En lugar de almacenar el estado actual (por ejemplo, "saldo: 1000 rublos"), almacenamos una secuencia de eventos: "Depósito de 500", "Compra de 200", "Cargo de comisión de 50".
\n\nEl estado actual es simplemente el resultado de aplicar todos los eventos en orden. Esto otorga un superpoder: podemos "rebobinar" el tiempo, restaurar el estado en cualquier fecha o analizar el comportamiento de los usuarios.
\n\nEjemplo de la vida real: cuenta bancaria
\n// Evento\ndata class MoneyDeposited(val amount: BigDecimal, val timestamp: Instant)\ndata class MoneyWithdrawn(val amount: BigDecimal, val timestamp: Instant)\n\n// Agregado (entidad)\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("El monto debe 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("Fondos insuficientes")\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\nCómo funcionan juntos CQRS y Event Sourcing
\n\nEste es un tándem clásico. CQRS se encarga de separar los canales, y Event Sourcing se encarga del almacen