Parece haver um amplo consenso na comunidade OOP de que o construtor de classes não deve deixar um objeto parcial ou totalmente não inicializado.
O que quero dizer com "inicialização"? Grosso modo, o processo atômico que leva um objeto recém-criado a um estado em que todos os seus invariantes de classe se mantêm. Deve ser a primeira coisa que acontece com um objeto (ele deve ser executado apenas uma vez por objeto) e nada deve ter permissão para se apossar de um objeto não inicializado. (Portanto, o conselho frequente de executar a inicialização do objeto diretamente no construtor da classe. Pelo mesmo motivo, os
Initialize
métodos geralmente são desaprovados, pois quebram a atomicidade e tornam possível obter e usar um objeto que ainda não está em um estado bem definido.)
Problema: Quando o CQRS é combinado com a fonte de eventos (CQRS + ES), onde todas as alterações de estado de um objeto são capturadas em uma série ordenada de eventos (fluxo de eventos), fico me perguntando quando um objeto realmente atinge um estado totalmente inicializado: No final do construtor da classe, ou após o primeiro evento ter sido aplicado ao objeto?
Nota: Não uso o termo "raiz agregada". Se preferir, substitua-o sempre que ler "objeto".
Exemplo para discussão: Suponha que cada objeto seja identificado exclusivamente por algum Id
valor opaco (pense em GUID). Um fluxo de eventos representando as alterações de estado desse objeto pode ser identificado no armazenamento de eventos pelo mesmo Id
valor: (Não vamos nos preocupar com a ordem correta do evento.)
interface IEventStore
{
IEnumerable<IEvent> GetEventsOfObject(Id objectId);
}
Suponha ainda que existem dois tipos de objetos Customer
e ShoppingCart
. Vamos nos concentrar em ShoppingCart
: Quando criados, os carrinhos de compras estão vazios e devem estar associados a exatamente um cliente. Esse último bit é uma classe invariável: um ShoppingCart
objeto que não está associado a Customer
está em um estado inválido.
Na OOP tradicional, pode-se modelar isso no construtor:
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;
}
}
No entanto, não sei como modelar isso no CQRS + ES sem terminar com a inicialização adiada. Como esse pequeno pedaço de inicialização é efetivamente uma alteração de estado, não precisa ser modelado como um 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, esse teria que ser o primeiro evento no ShoppingCart
fluxo de eventos de qualquer objeto, e esse objeto só seria inicializado quando o evento fosse aplicado a ele.
Portanto, se a inicialização se tornar parte do fluxo de eventos "playback" (que é um processo muito genérico que provavelmente funcionaria da mesma forma, seja para um Customer
objeto ou ShoppingCart
objeto ou qualquer outro tipo de objeto) ...
- O construtor deve ter menos parâmetros e não fazer nada, deixando todo o trabalho para algum
void Apply(CreatedEmptyShoppingCart)
método (que é praticamente o mesmo que o desaprovadoInitialize()
)? - Ou o construtor deve receber um fluxo de eventos e reproduzi-lo (o que torna a inicialização atômica novamente, mas significa que o construtor de cada classe contém a mesma lógica genérica de "reproduzir e aplicar", ou seja, duplicação indesejada de código)?
- Ou deveria haver um construtor OOP tradicional (como mostrado acima) que inicializa corretamente o objeto e, em seguida, todos os eventos, exceto o primeiro, estão
void Apply(…)
ligados a ele?
Não espero que a resposta forneça uma implementação de demonstração totalmente funcional; Eu já ficaria muito feliz se alguém pudesse explicar onde meu raciocínio é defeituoso ou se a inicialização do objeto é realmente um "ponto problemático" na maioria das implementações do CQRS + ES.
Initialize
método) teriam ocupado. Isso me leva à pergunta: como seria sua fábrica?