sourcing net event cqrs event-sourcing

cqrs - net - Usar un RDBMS como almacenamiento de eventos



event sourcing wikipedia (4)

Si estuviese usando un RDBMS (por ejemplo, SQL Server) para almacenar datos de eventos, ¿cómo sería el esquema?

He visto algunas variaciones habladas en un sentido abstracto, pero nada concreto.

Por ejemplo, digamos que uno tiene una entidad de "Producto", y los cambios a ese producto podrían venir en la forma de: Precio, Costo y Descripción. Estoy confundido acerca de si hubiera:

  1. Tenga una tabla "ProductoEvent", que tiene todos los campos para un producto, donde cada cambio significa un nuevo registro en esa tabla, además de "quién, qué, dónde, por qué, cuándo y cómo", según corresponda. Cuando se cambian el costo, el precio o la descripción, se agrega una nueva fila para representar el Producto.
  2. Almacene el costo, el precio y la descripción del producto en tablas separadas unidas a la tabla Producto con una relación de clave externa. Cuando se producen cambios en esas propiedades, escriba nuevas filas con WWWWWH, según corresponda.
  3. Almacene WWWWWH, más un objeto serializado que represente el evento, en una tabla "ProductoEvent", lo que significa que el evento en sí debe ser cargado, deserializado y reproducido en el código de mi aplicación para reconstruir el estado de la aplicación para un Producto dado .

En particular, me preocupa la opción 2 anterior. Llevado al extremo, la tabla de productos sería casi de una tabla por propiedad, donde cargar el estado de la aplicación para un producto determinado requeriría cargar todos los eventos para ese producto de cada tabla de eventos del producto. Esta explosión de mesa me huele mal.

Estoy seguro de que "depende", y aunque no hay una única "respuesta correcta", estoy tratando de tener una idea de lo que es aceptable, y lo que no es totalmente aceptable. También soy consciente de que NoSQL puede ayudar aquí, donde los eventos pueden almacenarse contra una raíz agregada, lo que significa solo una solicitud a la base de datos para obtener los eventos para reconstruir el objeto, pero no estamos usando un db NoSQL en el momento así que estoy buscando alternativas.


Bueno, quizás quieras echarle un vistazo a Datomic.

Datomic es una base de datos de hechos flexibles basados ​​en el tiempo , que admite consultas y combinaciones, con escalabilidad elástica y transacciones ACID.

Escribí una respuesta detallada here

Puedes ver una charla de Stuart Halloway explicando el diseño de Datomic here

Dado que Datomic almacena hechos a tiempo, puede usarlo para casos de uso de abastecimiento de eventos, y mucho más.


El proyecto GitHub CQRS.NET tiene algunos ejemplos concretos de cómo podría hacer EventStores en algunas tecnologías diferentes. Al momento de escribir, hay una implementación en SQL que usa Linq2SQL y un esquema de SQL para ir con él, hay uno para MongoDB , uno para DocumentDB (CosmosDB si estás en Azure) y uno que usa EventStore (como se mencionó anteriormente). Hay más en Azure, como Table Storage y Blob Storage, que es muy similar al almacenamiento de archivos planos.

Supongo que el punto principal aquí es que todos se ajustan al mismo principal / contrato. Todos ellos almacenan información en un solo lugar / contenedor / mesa, usan metadatos para identificar un evento de otro y ''solo'' almacenan todo el evento tal como estaba, en algunos casos serializado, en tecnologías de soporte, tal como estaban. Dependiendo de si elige una base de datos documental, una base de datos relacional o incluso un archivo plano, hay varias maneras diferentes de llegar al mismo objetivo de una tienda de eventos (es útil si cambia de opinión en algún momento y encuentra que necesita migrar o soporte). más de una tecnología de almacenamiento).

Como desarrollador del proyecto, puedo compartir algunas ideas sobre algunas de las elecciones que hicimos.

En primer lugar encontramos (incluso con UUID / GUID únicos en lugar de enteros) por muchas razones, los ID secuenciales se producen por razones estratégicas, por lo que tener una ID no era lo suficientemente único para una clave, por lo que fusionamos nuestra columna clave de ID principal con los datos / tipo de objeto para crear lo que debería ser una clave única (en el sentido de su aplicación). Sé que algunas personas dicen que no es necesario que lo almacene, pero eso dependerá de si usted está en el campo nuevo o de que tiene que coexistir con los sistemas existentes.

Nos quedamos con un solo contenedor / tabla / colección por razones de mantenimiento, pero sí jugábamos con una tabla separada por entidad / objeto. Encontramos en la práctica que significaba que la aplicación necesitaba permisos de "CREAR" (lo que generalmente no es una buena idea ... generalmente, siempre hay excepciones / exclusiones) o cada vez que una nueva entidad / objeto entraba en existencia o se implementaba, nueva contenedores de almacenamiento / tablas / colecciones que deben realizarse. Descubrimos que esto era muy lento para el desarrollo local y problemático para los despliegues de producción. Puede que no, pero esa era nuestra experiencia en el mundo real.

Otra cosa para recordar es que al hacer que la acción X ocurra, pueden ocurrir muchos eventos diferentes, conociendo todos los eventos generados por un comando / evento / lo que sea útil. También pueden estar en diferentes tipos de objetos, por ejemplo, presionar "comprar" en un carro de compras puede desencadenar eventos de cuenta y depósito para disparar. Una aplicación que consuma puede querer saber todo esto, así que agregamos un Id de correlación. Esto significaba que un consumidor podría solicitar todos los eventos planteados como resultado de su solicitud. Verás eso en el esquema .

Específicamente con SQL, encontramos que el rendimiento realmente se convirtió en un cuello de botella si los índices y las particiones no se usaban adecuadamente. Recuerde que los eventos deberán transmitirse en orden inverso si está usando instantáneas. Probamos algunos índices diferentes y descubrimos que, en la práctica, se necesitaban algunos índices adicionales para depurar las aplicaciones del mundo real en producción. De nuevo, verá eso en el esquema .

Otros metadatos en producción fueron útiles durante las investigaciones basadas en la producción, las marcas de tiempo nos dieron una idea del orden en el que los eventos fueron persistentes o no. Eso nos brindó asistencia en un sistema particularmente impulsado por eventos que generó grandes cantidades de eventos, brindándonos información sobre el rendimiento de cosas como las redes y la distribución de sistemas a través de la red.


La sugerencia posible es que el diseño seguido de "Dimensión que cambia lentamente" (tipo = 2) debería ayudarlo a cubrir:

  • orden de los eventos que ocurren (a través de la clave sustituta)
  • durabilidad de cada estado (válido de - válido a)

La función de plegado a la izquierda también debería estar bien para implementarla, pero debes pensar en la complejidad de la consulta futura.


La tienda de eventos no debería necesitar conocer los campos o propiedades específicos de los eventos. De lo contrario, cada modificación de su modelo daría lugar a tener que migrar su base de datos (al igual que en la buena persistencia tradicional basada en el estado). Por lo tanto, no recomendaría la opción 1 y 2 en absoluto.

A continuación se muestra el esquema utilizado en Ncqrs . Como puede ver, la tabla "Eventos" almacena los datos relacionados como un CLOB (es decir, JSON o XML). Esto corresponde a su opción 3 (Solo que no hay una tabla "ProductosEventos" porque solo necesita una tabla genérica de "Eventos" .En Ncqrs, la asignación a sus Raíces agregadas ocurre a través de la tabla "Fuentes de eventos", donde cada FuenteDeSuceso corresponde a una Raíz agregada).

Table Events: Id [uniqueidentifier] NOT NULL, TimeStamp [datetime] NOT NULL, Name [varchar](max) NOT NULL, Version [varchar](max) NOT NULL, EventSourceId [uniqueidentifier] NOT NULL, Sequence [bigint], Data [nvarchar](max) NOT NULL Table EventSources: Id [uniqueidentifier] NOT NULL, Type [nvarchar](255) NOT NULL, Version [int] NOT NULL

El mecanismo de persistencia SQL de la implementación de la Tienda de Eventos de Jonathan Oliver consiste básicamente en una tabla llamada "Commits" con un campo BLOB "Payload". Esto es más o menos lo mismo que en Ncqrs, solo que serializa las propiedades del evento en formato binario (que, por ejemplo, agrega compatibilidad con encriptación).

Greg Young recomienda un enfoque similar, como ampliamente documentado en el sitio web de Greg .

El esquema de su tabla prototípica de "Eventos" dice:

Table Events AggregateId [Guid], Data [Blob], SequenceNumber [Long], Version [Int]