Parece haber un acuerdo generalizado en la comunidad OOP de que el constructor de la clase no debe dejar un objeto parcial o incluso sin inicializar.
¿Qué quiero decir con "inicialización"? En términos generales, el proceso atómico que lleva a un objeto recién creado a un estado en el que se encuentran todos sus invariantes de clase. Debe ser lo primero que le sucede a un objeto (solo debe ejecutarse una vez por objeto) y no se debe permitir que nada se apodere de un objeto no inicializado. (Por lo tanto, el consejo frecuente de realizar la inicialización de objetos directamente en el constructor de la clase. Por la misma razón, los
Initialize
métodos a menudo están mal vistos, ya que estos separan la atomicidad y hacen posible agarrar y usar un objeto que aún no está en un estado bien definido)
Problema: cuando CQRS se combina con el abastecimiento de eventos (CQRS + ES), donde todos los cambios de estado de un objeto se capturan en una serie ordenada de eventos (secuencia de eventos), me pregunto cuándo un objeto realmente alcanza un estado completamente inicializado: ¿Al final del constructor de la clase, o después de que el primer evento se haya aplicado al objeto?
Nota: me abstengo de usar el término "raíz agregada". Si lo prefiere, sustitúyalo cada vez que lea "objeto".
Ejemplo de discusión: Suponga que cada objeto está identificado de manera única por algún Id
valor opaco (piense en GUID) Un flujo de eventos que representa los cambios de estado de ese objeto se puede identificar en el almacén de eventos con el mismo Id
valor: (No nos preocupemos por el orden correcto de los eventos).
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
Supongamos además que hay dos tipos de objetos Customer
y ShoppingCart
. Centrémonos en ShoppingCart
: Cuando se crean, los carritos de compras están vacíos y deben estar asociados con exactamente un cliente. Ese último bit es una clase invariante: un ShoppingCart
objeto que no está asociado a un Customer
está en un estado no válido.
En OOP tradicional, uno podría modelar esto en el constructor:
partial class ShoppingCart
{
public Id Id { get; private set; }
public Customer Customer { get; private set; }
public ShoppingCart(Id id, Customer customer)
{
this.Id = id;
this.Customer = customer;
}
}
Sin embargo, no sé cómo modelar esto en CQRS + ES sin terminar con la inicialización diferida. Dado que este simple bit de inicialización es efectivamente un cambio de estado, ¿no tendría que modelarse como un evento ?:
partial class CreatedEmptyShoppingCart
{
public ShoppingCartId { get; private set; }
public CustomerId { get; private set; }
}
// Note: `ShoppingCartId` is not actually required, since that Id must be
// known in advance in order to fetch the event stream from the event store.
Obviamente, este tendría que ser el primer evento en ShoppingCart
la secuencia de eventos de cualquier objeto, y ese objeto solo se inicializaría una vez que se le aplicara el evento.
Entonces, si la inicialización se convierte en parte de la "reproducción" de la secuencia de eventos (que es un proceso muy genérico que probablemente funcionaría igual, ya sea para un Customer
objeto o un ShoppingCart
objeto o cualquier otro tipo de objeto) ...
- ¿Debería el constructor no tener parámetros y no hacer nada, dejando todo el trabajo a algún
void Apply(CreatedEmptyShoppingCart)
método (que es muy parecido al mal vistoInitialize()
)? - ¿O debería el constructor recibir un flujo de eventos y reproducirlo (lo que hace que la inicialización sea atómica nuevamente, pero significa que el constructor de cada clase contiene la misma lógica genérica de "reproducir y aplicar", es decir, duplicación de código no deseada)?
- ¿O debería haber un constructor tradicional de OOP (como se muestra arriba) que inicializa correctamente el objeto, y luego todos los eventos excepto el primero están
void Apply(…)
ligados a él?
No espero que la respuesta proporcione una implementación demo totalmente funcional; Ya estaría muy feliz si alguien pudiera explicar dónde está defectuoso mi razonamiento, o si la inicialización de objetos es realmente un "punto débil" en la mayoría de las implementaciones de CQRS + ES.
Initialize
método) habrían ocupado. Eso me lleva a la pregunta, ¿cómo podría ser una fábrica tuya?