2 puntos por GN⁺ 2023-12-18 | 1 comentarios | Compartir por WhatsApp
  • El modelo relacional CRUD muestra bien la estructura de almacenamiento, pero tiende a sobrescribir los procesos de negocio, lo que dificulta rastrear qué ocurrió realmente en el sistema
  • Event Sourcing deja como Event Stream los eventos inmutables que se producen después de cada operación, y luego lee esa lista en la toma de decisiones para determinar el estado actual
  • El modelado avanza identificando primero los eventos y luego conectando la orden (command), es decir, la intención de acción del usuario, con las reglas de negocio para entender el proceso
  • Al buscar candidatos a eventos en datos relacionales existentes, conviene revisar columnas de estado, columnas de fecha, si admiten nulos y relaciones 1:N, pero es peligroso asumir que solo con los valores de estado se puede reconstruir un historial completo
  • Al migrar datos que solo conservan el estado final, es más realista empezar con un evento de importación explícito como Order Imported, en lugar de intentar reconstruir a la fuerza eventos pasados, y validar de forma iterativa en un entorno seguro

Ver datos CRUD como un modelo centrado en eventos

  • El modelo de datos relacional muestra qué datos se almacenan, pero dificulta entender qué ocurrió dentro del sistema y cómo interactúan los procesos
  • El enfoque CRUD tradicional puede perder información importante de negocio al sobrescribir datos
  • Event Sourcing prioriza la calidad de la información por encima del tamaño de almacenamiento y guarda como eventos los hechos ocurridos después de cada operación

Modelo básico de Event Sourcing

  • Un evento es un hecho sobre algo que ya ocurrió y, una vez almacenado, es un dato inmutable que no puede modificarse
  • Un Event Stream es una lista ordenada de todo lo ocurrido sobre un registro
  • No se pueden modificar eventos pasados, pero sí se puede corregir un error anterior agregando un nuevo evento al final
  • Al tomar decisiones, se lee y revisa la lista de eventos para determinar el estado actual y la siguiente acción

Orden del modelado de procesos

  • El modelado empieza primero con el descubrimiento de eventos
  • Después se identifican los comandos (command) y se define qué acción se intenta ejecutar
  • Por último, se ordenan las reglas de negocio
  • Los eventos se convierten en el eje central para que responsables técnicos y de negocio entiendan juntos el proceso
  • Como en EventStorming de Alberto Brandolini, se puede entender el proceso observando juntos eventos, comandos y reglas

Buscar candidatos a eventos en datos relacionales existentes

  • 1. Revisar columnas de estado

    • Los valores de una columna status pueden reflejar etapas del ciclo de vida de los datos
    • Si una orden tiene estados como initiated, shipped o paid, cada uno puede ser candidato a eventos como Order Initiated, Order Shipped y Order Paid
    • Sin embargo, un valor de estado puede ser una interpretación que aplana el proceso de negocio, por lo que no debe asumirse que está completo
    • Hay que evitar nombrar eventos como Order Created, Order Updated o Order Deleted, como si fueran operaciones CRUD
    • State Obsession se presenta como un enfoque que conviene evitar
  • 2. Revisar columnas de fecha

    • Las columnas de fecha pueden indicar momentos importantes de ocurrencia dentro del ciclo de vida del proceso
    • CreatedDate y ModifiedDate no aportan mucha información, pero ShipmentDate, DeliveryDate y OrderPlacementDate son mejores pistas
    • Ejemplos:
      • ShipmentDate puede ser una pista para introducir el evento Order Shipped
      • OrderPlacementDate sugiere que Order Placed podría ser un mejor nombre que Order Initiated
      • DeliveryDate muestra que podría necesitarse un evento Order Delivered
    • Estas pistas deben validarse con expertos del dominio y alinearse con el proceso de negocio real
  • 3. Analizar si las columnas admiten nulos

    • Una columna non-nullable contiene datos que siempre deben proporcionarse
    • Una columna nullable puede proporcionarse más tarde en otra operación o ser un valor opcional
    • Si una columna es obligatoria en el Ordering Process, ese dato también debería incluirse en el primer evento Order Initiated
    • Un único tipo de evento no siempre es el punto de inicio del stream; puede haber varios eventos iniciales
  • 4. Identificar tablas con muchas relaciones 1:N

    • Para encontrar límites de streams, se puede empezar por las tablas que tienen muchas relaciones 1:N
    • Las tablas con muchas relaciones “one” son candidatas a tipos de stream
    • También hay que evaluar lógicamente si los datos pueden existir de forma independiente entre sí
      • shipment puede ser un proceso separado de order
      • order line difícilmente puede existir sin order
    • Al discutir los límites, se pueden descubrir más eventos y ampliar la comprensión del proceso

No crear eventos falsos durante la migración

  • Los datos relacionales son un estado final aplanado, por lo que intentar deducir eventos pasados detallados a partir de ese estado puede fallar o ser impreciso
  • En lugar de crear a la fuerza pequeños eventos históricos, es mejor proporcionar explícitamente un evento Order Imported que contenga todo el estado actual y el código de interpretación
  • Un evento de importación muestra claramente de qué forma ingresaron los datos y puede ser importante para la resolución de problemas y el diagnóstico

Validar con un prototipo

  • La migración debe probarse como prototipo en un entorno seguro y verificarse cómo se comporta realmente el modelo
  • Hay que comparar los resultados con los valores esperados y corregir de forma iterativa
  • Se necesita un enfoque que no se apresure ni pierda la información existente, y que use esa información para mejorar el modelo posterior
  • La estrategia general para pasar de datos relacionales a un enfoque basado en documentos también se conecta con General strategy for migrating relational data to document-based

1 comentarios

 
GN⁺ 2023-12-18
Comentarios de Hacker News
  • 2c: si otras partes de la app también necesitan PostgreSQL, conviene guardar los datos de eventos también en PostgreSQL + herramientas de reporting FOSS (Apache Superset, Metabase, etc.) y aguantar más o menos hasta 2 TB
    Después de eso, se puede decidir si realmente hace falta mantener esos 2 TB completos en línea, o si basta con resúmenes por día/hora. Si es lo segundo, seguir con PostgreSQL sigue siendo más que suficiente[1]
    Un cliente procesa más de 10 TB, 1,500 eventos por segundo, 600 bytes por registro (80 GB diarios antes de indexar), mantiene en línea solo 2 días de datos detallados, resume el resto y mueve el detalle a S3 para seguir consultándolo con Athena SQL[2]
    Incluyendo hasta el portal de reportes para clientes, el costo total es menor a 2 mil dólares, y en AWS RDS con failover automático multi-AZ (db.m7g.2xlarge) maneja tanto inserciones como consultas de reporting con menos de 2% de carga
    Como el equipo de negocio arma por su cuenta los charts y gráficos, un solo ingeniero dedica menos de 5 horas al mes al mantenimiento
    Si usas herramientas propietarias, tal vez algunos gráficos vengan “incluidos”, pero con pgsql los datos están en un solo lugar, hay un solo sistema que aprender, un solo sistema que mantener en línea/replicar/respaldar/recuperar, un solo sistema que asegurar/escalar, un solo proveedor que gestionar, y hay millones de ingenieros que conocen este sistema
    En sistemas como Preset o Metabase, crear 12 gráficos toma una hora, y hasta personal no técnico puede hacerlo
    Como referencia, aunque tengo sesgo, llevo más de 20 años viendo aparecer y desaparecer bases de datos y sistemas de reporting, y el buen viejo PostgreSQL mejora cada año
    https://instances.vantage.sh/aws/rds/db.m7g.2xlarge?region=u...
    [1] Si de verdad hace falta, también hay sistemas compatibles con PostgreSQL para escalar más. Aurora puede escalar 3–5x, TimescaleDB 10x, y CitusDB 10x+. Cada uno tiene el costo de volverse un poco no estándar, así que no lo recomiendo antes de que realmente haga falta
    [2] El dashboard de reporting para clientes necesita responder en menos de 1 segundo, y eso lo entrega PostgreSQL consultando tablas resumidas indexadas. Athena responde en alrededor de 1–2 segundos con escaneos paralelos

    • En esta dirección, incluso cuando se necesita “viaje en el tiempo”, “recuperación de estado sobrescrito” o “reinterpretación de eventos pasados”, a veces solo con un log de auditoría basta
      Solo hay que mantener snapshots de los datos antes de guardarlos, tener scripts para identificar y recopilar secuencias específicas de eventos, y luego que una persona revise y aplique retroactivamente a gran escala el efecto de la nueva lógica
      Herramientas como https://django-simple-history.readthedocs.io/en/latest/ son una solución medio confiable y simple para crear tablas de auditoría, y si también necesitas auditar acceso directo a la base de datos, puedes agregar triggers de Postgres
      En teoría me gusta el event sourcing, pero en la práctica tiene demasiado boilerplate para agregar nuevos flujos CRUD o para desplegar rápido y de forma confiable las intervenciones y hotfixes que una startup en etapa inicial o media suele necesitar en situaciones inesperadas
      A menos que estés implementando algo como rieles de procesamiento de pagos, puede que event sourcing no sea la elección correcta
      https://news.ycombinator.com/item?id=17817375 (2018) también tiene una buena conversación sobre las desventajas de event sourcing
    • Este comentario vale más que el artículo mismo
      El único problema de PostgreSQL es que del lado de inserción tiene unos problemas de escalabilidad interesantes. Normalmente recomiendo poner una cola entre la fuente de eventos y la DB
    • Me pregunto si en general se trata de tener una tabla definida como {id:uuid,created_at:timestamptz,data:jsonb}
      Sobre todo cuando la estructura de los eventos es variada y la definición de los eventos va cambiando, es difícil sacarle buen provecho a las capacidades de indexación de JSONB
      Supongo que necesito familiarizarme más con esta documentación: https://www.postgresql.org/docs/current/datatype-json.html#J...
    • Me gustaría saber dónde encontrar textos detallados sobre cómo construir un sistema así
  • Hace tiempo en el equipo evaluamos con bastante fuerza el event sourcing, pero a mí me parecía una solución en busca de un problema
    Podría haber funcionado también para nosotros, pero sus ventajas no eran inmediatamente claras y los riesgos y tropiezos de adoptar una forma nueva no parecían lo mejor para el proyecto ni para la empresa, así que al final lo descartamos
    Quizá fue una decisión tipo “herramienta” con la que perdimos una oportunidad de aprendizaje, pero no me arrepiento de no haberme metido en esa madriguera de conejo cuando no había un zorro persiguiéndonos por detrás

    • Un sistema aburrido, tradicional y que funciona bien es una amenaza para un equipo de ingeniería sobredimensionado que no tiene nada que hacer y anda puliendo cosas para poner en el CV
      Ese es el “problema” que esta solución resuelve
    • Por ejemplo, una base de datos temporal tiene bastante sentido para datos financieros
      Pero en la mayoría de los casos basta con usar una base de datos normal y guardar el historial de cambios pasados en una tabla auxiliar. Entonces la base de datos principal funciona como una especie de vista materializada
    • Casi todos los datos que se guardan en SQL en realidad encajarían mejor en una base de datos de documentos, pero como nadie está familiarizado con eso, se sigue igual
      No me molesta demasiado, y tampoco diría que haya sido una mala decisión, pero sí genera problemas en la forma de manejar cambios en el modelo de datos
      Me parece que la mayoría de las formas de almacenamiento de datos no han seguido el ritmo de cómo se construye el software hoy en día, y cosas como eventos y colas son el resultado de montar encima de los sistemas existentes la funcionalidad que hacía falta
      Hoy muchas relaciones de datos ocurren entre varios servicios, o sea fuera de la base de datos. Así está hecho el entorno moderno de TI en muchas organizaciones
      Hay datos maestros internos que dan soporte a varios equipos de negocio, e interactúan con más de 300 sistemas y aplicaciones de TI para simplificar el trabajo
      Con microservicios es fácil mantener limpia la lógica de negocio y el modelo de datos, pero a cambio hay que gestionar eventos, colas, estado de datos e incluso almacenes dependientes, y ahora mismo eso es demasiado complejo
      Me gusta SQL, pero sinceramente siento que casi todo lo que construimos hoy en día cabría perfectamente en SQLite
  • Lo que suele faltar en estas discusiones es cuándo conviene una arquitectura dirigida por eventos
    En corto: si el cliente hizo algo y espera una respuesta, eso no es dirigido por eventos sino simplemente request/response
    Lo dirigido por eventos es cuando algo pasa fuera de banda. Por ejemplo, haces push de código a GitHub y eso dispara un build
    En este ejemplo, refrescar la página para ver el código actualizado es request/response, pero el build de CI que entra en la cola sí es dirigido por eventos
    Ojalá sirva

    • No es tan simple. El hecho de que sea request-response no es el criterio para elegir event sourcing o una arquitectura dirigida por eventos
      Incluso con event sourcing o una arquitectura dirigida por eventos puedes crear flujos request/response, inline, bloqueantes y circulares
      Al revés, también puedes construir asincronía perfectamente sin event sourcing ni arquitectura dirigida por eventos, usando workers, colas, actores o multithreading
  • Modelar eventos de dominio es útil para explicarles el problema que se quiere resolver a los expertos del dominio, y puede que lo correcto sea dejarlo documentado al planear la solución
    Para implementar de verdad un sistema que proporcione una pista de auditoría de una máquina de estados de larga duración, probablemente sea mejor usar algo como Temporal.io o durable functions
    Estas herramientas usan event sourcing en su persistencia interna y ofrecen un modelo de programación que añade restricciones distintas al código que orquesta la funcionalidad (workflows) y al código que interactúa con el mundo real (activities), obligándote a pensar en deduplicación e idempotencia

    • Las durable functions tienen el problema de que les falta observabilidad
      Me gustaría escuchar sugerencias sobre cómo superar eso
  • El concepto es interesante, pero el artículo no logra explicar bien cómo funciona
    Me da curiosidad cómo se reconstruye eficientemente el estado actual a partir del stream de eventos y cómo se modela ese stream de eventos en una base de datos

    • El autor tiene varias presentaciones
      https://www.youtube.com/watch?v=gG6DGmYKk4I
      https://www.youtube.com/watch?v=jnDchr5eabI
      https://www.youtube.com/watch?v=ArcypYS5XBQ
      https://www.youtube.com/watch?v=uODSwR2CIV4
      También mantiene ejemplos en GitHub
      https://github.com/oskardudycz/EventSourcing.NetCore
      https://github.com/oskardudycz/EventSourcing.NodeJS
      https://github.com/oskardudycz/EventSourcing.JVM
    • Hay dos formas
      La primera es usar una base de datos diseñada para este tipo de uso. Están Google BigQuery, Amazon Redshift, ClickHouse y otras
      Todos los datos actuales son, en esencia, algún tipo de agregación. Dicho de otro modo, son como una consulta group-by sobre una base de datos de eventos
      Si existen los eventos, tiene sentido porque técnicamente puedes reconstruir el estado actual o un estado pasado con consultas de agregación
      La segunda es renombrar el almacén relacional y llamarlo una capa de caché al lado del sistema de eventos
      Funcionalmente es lo mismo, pero no enciende las alarmas de la gente obsesionada con que todo tenga que ser event-driven
      La arquitectura descrita en el artículo sí existe. Solo que es tremendamente compleja, así que los servicios que la aprovechan normalmente hacen trabajos muy específicos. Piensa en cosas como Google Analytics, Datadog o Splunk
    • No existe un solo “estado actual”. Esa forma de pensar viene de centralizar todo en una sola DB
      Puedes crear distintos estados en distintos sistemas según diferentes necesidades
      Si estuvieras construyendo un sistema de compras, con compras y clientes, un servicio podría leer eventos y construir tablas relacionales para fines financieros
      Otro servicio podría leer eventos y construir un almacén clave-valor de datos de clientes, y un tercero podría operar un servicio OpenSearch para búsqueda de productos
      Un stream de eventos es una lista. Si usas algo adecuado para ese propósito, como Kafka, entonces son varias listas, es decir, topics, particiones, etc.
    • Tiene más sentido usarlo para ciertos streams donde hay mucho cambio y donde los datos son lo bastante interesantes como para ver qué pasó en el camino
      Pero incluso eso puede resolverse dentro del modelo relacional
  • Esto es una diferencia entre top-down vs bottom-up, o entre personalizado vs de propósito general
    El enfoque top-down parte del dominio de negocio y luego mapea la implementación sobre las tecnologías, herramientas y vendors disponibles
    El enfoque bottom-up parte de las tecnologías, herramientas y vendors disponibles, y las ensambla para crear una solución que funcione
    En lo personalizado están DDD, CQRS/ES, Sagas, TBUI (UI basada/dirigida por tareas), GraphQL, tipos de datos algebraicos, etc.
    En lo de propósito general están RDBMS, CRUD, REST, transacciones ACID, CDC, UI administrativa genérica, no-code/low-code, tipos restringidos/genéricos, etc.

  • Yo simplemente seguiría usando los buenos viejos datos relacionales

    • Está bien, hazlo hasta que deje de funcionar. La idea es no usar un martillo para un tornillo
  • Estoy de acuerdo con la arquitectura basada en eventos, pero este artículo parece tener dificultades para transmitir el punto
    Yo me enfocaría en la diferencia entre las relaciones de datos y el comportamiento de negocio
    Cuando empiezas a pensar en términos de comportamiento y actividad de negocio, el movimiento para alejarse de un almacén relacional operativo se vuelve mucho más claro

    • En un nivel abstracto, los eventos también pueden modelarse como relaciones
  • El event sourcing tiene varias propiedades buenas, así que sí resulta interesante
    Pero, ¿igual no siguen haciendo falta las relaciones? Si es así, ¿entonces cómo se implementan esas relaciones?
    Si la respuesta es “todo está implícitamente metido en código de la capa de aplicación”, me cuesta aceptarlo
    Aun así necesitas consultar relaciones, mantener actualizadas vistas relacionales, o algo parecido
    Está bien si las relaciones no son el núcleo del modelo de persistencia, pero tienen que implementarse en algún lugar de la capa de datos, y aquí no veo que se mencione eso
    Firestore tiene el mismo problema. Todo el mundo maneja las relaciones de una forma u otra, pero al final se convierte en código de aplicación espagueti que no escala

    • En un sistema de event sourcing, el stream de eventos se proyecta en modelos de lectura, y esos modelos de lectura pueden ser varios: relacionales, de series de tiempo, etc.
      Si estás familiarizado con la programación funcional, es esencialmente lo mismo que una operación fold que reduce un stream de eventos a un estado
      Por lo que he visto al trabajar antes con sistemas de event sourcing, la ventaja de tener un historial de eventos almacenado explícitamente existe, pero también aumenta bastante la complejidad
      Empiezan a aparecer problemas como cómo generar realmente los modelos de lectura, cómo gestionar las versiones del modelo, si conviene tener snapshots de los modelos de lectura, etc.
      En mi experiencia, en la mayoría de los contextos donde se aplicó este patrón, la complejidad adicional no valía la pena
  • Lo que se necesita es una cola de comandos. Los eventos de comando no son eventos de dominio