CQRS y Event Sourcing en Swift: Arquitectura para proyectos complejos

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Por qué la arquitectura tradicional no puede con las tareas modernas?

\n\n

¿Alguna vez te has encontrado en una situación donde agregar una nueva funcionalidad a una aplicación Swift se convierte en una pesadilla? ¿Los errores se acumulan, los datos se pierden y la lógica crece hasta alcanzar proporciones increíbles? ¿Te suena familiar? Bienvenido al mundo donde CQRS y Event Sourcing se convierten en tus mejores amigos.

\n\n

Estos patrones no son solo palabras de moda. Son una forma de pensar que cambia radicalmente el enfoque para construir aplicaciones en Swift. Y hoy los analizaremos con ejemplos reales. Sin rodeos, sin teorías complicadas, solo práctica.

\n\n

¿Qué es CQRS? Divide y vencerás

\n\n

CQRS (Command Query Responsibility Segregation) es un principio que dice: "Basta de mezclar lectura y escritura en una misma clase". Suena simple, pero las consecuencias son enormes.

\n\n

En la arquitectura CRUD (Create, Read, Update, Delete) clásica, usas un solo modelo para todo. Por ejemplo, User. Se guarda, se lee y se actualiza. ¿Y qué pasa si para leer necesitas unos campos y para escribir otros? Empiezan los parches: campos opcionales, banderas, mapeos...

\n\n

CQRS propone separar la responsabilidad:

\n
    \n
  • Comandos (Commands) — modifican el estado. Mutan los datos, pero no devuelven nada.
  • \n
  • Consultas (Queries) — leen datos. No cambian el estado, solo devuelven un resultado.
  • \n
\n\n

En Swift, esto podría verse así:

\n\n
// Comando — solo mutación\nstruct CreateUserCommand {\n    let id: UUID\n    let name: String\n    let email: String\n}\n\n// Consulta — solo lectura\nstruct GetUserQuery {\n    let userId: UUID\n}\n\n// Manejador de comando\nclass CreateUserCommandHandler {\n    func handle(_ command: CreateUserCommand) {\n        // Lógica de creación de usuario\n        print("Usuario creado: \\(command.name)")\n    }\n}\n\n// Manejador de consulta\nclass GetUserQueryHandler {\n    func handle(_ query: GetUserQuery) -> User? {\n        // Lógica de obtención de usuario\n        return User(id: query.userId, name: "Alexey", email: "alex@example.com")\n    }\n}
\n\n

¿Ves? Sin mezclas. Cada clase hace exactamente una cosa. Esto simplifica las pruebas, el mantenimiento y la escalabilidad.

\n\n

Event Sourcing: Almacenamos la historia, no el estado

\n\n

Ahora pasamos a Event Sourcing — un patrón que no almacena el estado actual de un objeto, sino toda la secuencia de eventos que llevaron a ese estado.

\n\n

Imagina una cuenta bancaria. En lugar de almacenar el saldo (1000 rublos), almacenas una lista de eventos: "Apertura de cuenta", "Depósito de 500", "Retiro de 200", "Depósito de 700". Para conocer el saldo actual, simplemente reproduces todos los eventos. Esto ofrece posibilidades increíbles: auditoría, reversión de cambios, análisis.

\n\n

Implementemos esto en Swift:

\n\n
// Evento\nprotocol Event {}\n\nstruct AccountCreated: Event {\n    let accountId: UUID\n    let initialBalance: Double\n}\n\nstruct MoneyDeposited: Event {\n    let accountId: UUID\n    let amount: Double\n}\n\nstruct MoneyWithdrawn: Event {\n    let accountId: UUID\n    let amount: Double\n}\n\n// Agregado — restaura el estado a partir de eventos\nclass BankAccount {\n    private(set) var balance: Double = 0\n    private(set) var isActive: Bool = false\n    private(set) var accountId: UUID?\n\n    func apply(_ event: Event) {\n        switch event {\n        case let created as AccountCreated:\n            accountId = created.accountId\n            balance = created.initialBalance\n            isActive = true\n        case let deposited as MoneyDeposited:\n            balance += deposited.amount\n        case let withdrawn as MoneyWithdrawn:\n            balance -= withdrawn.amount\n        default:\n            break\n        }\n    }\n\n    // Restauración desde el historial de eventos\n    static func load(from history: [Event]) -> BankAccount {\n        l                

Blogs

Book Recommendations