Eventuous / eventuous

Event Sourcing library for .NET
https://eventuous.dev
Apache License 2.0
442 stars 70 forks source link

Unable to create isolated Aggregates with State and Id #290

Closed iappwebdev closed 9 months ago

iappwebdev commented 9 months ago

Hey,

I want to unit test in isolation my Aggregate inheriting from Aggregate<T> and having State and Id (located in Eventuous.Domain). I wanted to write a simply test of the aggregate separated from the CommandService but could not instantiate an aggregate with State and Id because the setter of Id in State is internal (what I unterstand). I looked in the source code and found the create method in the CommandService located in Eventuous.Application project.

TAggregate Create(TId id) => _factoryRegistry.CreateInstance<TAggregate, TState>().WithId<TAggregate, TState, TId>(id);

It is using an AggregateFactoryRegistry to create aggregates. The WithId() extension is located in Eventuous.Persistence in AggregateStoreExtensions. The internals of Eventuous.Domain are visible to Eventuous.Persistence. I thought I could use that and I tried to write a test:

[Fact]
public void TestOrder()
{
    var order = AggregateFactoryRegistry.Instance
        .CreateInstance<Order, OrderState>()
        .WithId(new OrderId("id123"));  // Error, WithId is interal...
    order.Should().NotBeNull();
    // Tests ...
}

Unfortunatly, WithId() is internal. It works without using WithId() but then I get an exception later on when calling aggregate commands like order.RemoveArtikel(ArtikelName name).

So my question is: how to instantiate an Aggregate with State and Id without having to mock the CommandService and the EventStore?

solomkae commented 9 months ago

This also makes difficult to use Aggregate without CommandService, when u read Aggregate using just AggregateStore then Aggregate ID is not initialised, and there is no simple way to set it as WithId is private.

alexeyzimarev commented 9 months ago

@solomkae you load the aggregate instance from somewhere, right? If you use aggregateStore.Load<TAggregate, State, TId>(streamNameMap, id, cancellationToken), it will assign the id. Same for LoadOrNew.

alexeyzimarev commented 9 months ago

@iappwebdev does the testing lib satisfy your need?

iappwebdev commented 9 months ago

@alexeyzimarev Yes, indeed thank you for the testing lib. Only thing that sould be changed is the use of Id instead of AggregateId, as it is deprecated.

public static TAggregate CreateTestAggregateInstance<TAggregate, TState, TId>(this AggregateFactoryRegistry registry, TId id) 
    where TAggregate : Aggregate<TState> where TState : State<TState>, new() where TId : AggregateId    // Use Id here
    => registry.CreateInstance<TAggregate>().WithId<TAggregate, TState, TId>(id);
iappwebdev commented 9 months ago

You included the InMemoryEventStore, that's nice!