CQRS-ES
Principio
La segregación de responsabilidades de comando y consulta con event sourcing es el patrón clave usado en las soluciones de procesamiento de pagos de IPF.
Justificación
-
Escrituras eficientes y no bloqueantes
Cada cambio de estado crítico se registra en la parte de escritura como eventos de dominio, de forma append-only. Como no hay necesidad de persistir y mantener un estado actual por separado, no hay necesidad de actualización in-place ni de implementar soluciones de bloqueo optimista, eliminando el riesgo de contención. -
Consultas eficientes del lado de lectura con modelos de vista complejos, sin impactar el lado de comando
Las consultas complejas para búsquedas y analítica se ejecutan en el lado de lectura separado. Aceptar la realidad de que siempre va a haber un elemento de retraso al leer/obtener datos y la consistencia eventual permite desacoplar los modelos de lectura y escritura. Se crean una variedad de modelos de lectura y proyecciones complejas obteniendo los datos del lado de escritura como parte de la proyección/sincronización, sin impactar el procesamiento de pagos en general. -
Escalado independiente de equipos y uso de tecnología
La separación del lado de lectura y del de escritura también permite un desacoplamiento más amplio de equipos y tecnología, proporcionando oportunidades para escalar diferentes equipos y aplicar tecnologías especializadas para mejorar la eficiencia de los equipos y de las soluciones de lectura y escritura. Es posible aplicar opciones y optimizaciones tecnológicas que pueden diferir entre el lado de lectura y el de escritura. -
No hay necesidad de mantener un historial de ejecución por separado
Los eventos de dominio son hechos inmutables que representan el cambio de estado. Dar a los eventos de dominio el papel crítico en la solución y construir el estado de la aplicación a partir de ellos también da la ventaja de mantener el historial de ejecución crítico del negocio como parte del procesamiento de pagos, no como un añadido posterior. -
Gestión de estado consistente
Los eventos de dominio se convierten en la única fuente de verdad, eliminando el riesgo de publicar un evento de dominio hacia fuera pero fallar al actualizar el estado de forma consistente. -
Mejor auto-sanación y resolución de problemas
En caso de fallos temporales, los eventos de dominio registrados pueden reproducirse de nuevo para recuperar el estado de la aplicación y permitir auto-sanación, depuración o resolución de problemas. -
El archivado es mucho más fácil con datos inmutables
Sin duda es mucho más seguro archivar eventos de dominio inmutables en un almacenamiento más lento. -
El modelo de objetos no tiene que ser igual que el modelo de datos
CQRS te guía naturalmente a modelar tu dominio con las preocupaciones correctas desde el principio. Los comandos se modelan para contener información concisa suficiente para decidir cómo manejar el comando. Esto permite que el modelo sea más enfocado y no se pueble con preocupaciones que no sirven al propósito.
Implicaciones
-
Abraza la consistencia eventual
Con CQRS, habrá un retraso natural entre las escrituras y las lecturas, ya que las actualizaciones de datos pueden no estar disponibles inmediatamente en el lado de lectura. Entiende los límites aceptables y aplica optimizaciones para reducir la latencia. Lo mejor es empezar cuestionando las razones detrás de los requisitos de consistencia fuerte. Puede quedar claro que todo ese esfuerzo y coste por una consistencia fuerte puede que no te dé el beneficio de negocio deseado; incluso puede degradar la aplicación resultando más costoso. -
Curva de aprendizaje pronunciada
Como con cualquier otra cosa, hay una curva de aprendizaje que requiere un cambio respecto al pensamiento de diseño tradicional. Sin embargo, el patrón no es nuevo y hay muchas implementaciones diferentes con ejemplos y artículos. -
Compatibilidad hacia atrás
En event sourcing, en realidad es más fácil introducir un nuevo estado ya que no impactará realmente el modelo general. Sin embargo, con el tiempo, puede que necesites aplicar más cambios rompientes como cambios obligatorios en la estructura de un evento. Un buen enfoque es ser flexible en lo que aceptas desde fuera y estricto al exponer datos hacia fuera. Esto puede permitir la introducción de adaptadores cuando sea necesario. Tomar snapshots antes de introducir cambios rompientes y difundir un evento mayor también son estrategias útiles. -
Mayor volumen de datos
Naturalmente, habrá más datos que la aplicación persistirá. Esto es principalmente porque el rastro de auditoría del 'cómo llegamos allí' ahora es la parte crítica del dominio. El archivado ya no es tan problemático porque es un log append-only. Eliminar los eventos después de difundirlos y hacer snapshot también podría ayudar con las preocupaciones de volumen de datos. -
Reconstruir el estado puede ser costoso
A medida que crece el número de eventos para representar los cambios de estado en un objeto de dominio, puede volverse ineficiente rehidratar todos los eventos del objeto para construir su estado actual. El snapshotting es útil y puede aplicarse a los eventos de dominio después de un cambio importante. Esto reduciría el número de eventos a rehidratar y mejoraría el rendimiento general. Otra opción podría ser mantener el objeto de dominio en memoria, quizá introduciendo una caché distribuida. Este es un enfoque arriesgado y debes asegurarte de que la caché se actualice de manera consistente a medida que se persisten los eventos de dominio. -
Desafíos en la comprobación de duplicados
La comprobación de duplicados se usa a menudo como una crítica al event sourcing. Como no hay gestión de datos de actualización in-place, puede ser más desafiante detectar entidades duplicadas. De nuevo, hay varias estrategias para superar eso. Una estrategia es permitir duplicados pero implementar un riguroso proceso posterior que maneje los datos duplicados. Sin embargo, esto no funcionará para pagos instantáneos ya que el movimiento de dinero es muy crítico y permitir pagos duplicados sería demasiado arriesgado. Otra estrategia podría ser usar el identificador único del pago como clave primaria para los eventos, no permitiendo eventos duplicados para el mismo payment ID. Sin embargo, como el payment id suele ser una combinación de campos obligatorios y opcionales, podría ser un reto implementar un identificador único así. Una mejor estrategia es mantener una caché temporal de objetos de pago cerca de la aplicación y los nuevos comandos de ejecución de pago podrían comprobarse contra los pagos en caché y rechazarse si se detecta duplicación.