Skip to content
Nelson Junior edited this page Jul 12, 2016 · 4 revisions

Welcome to the enjoy.cqrs wiki!

Setting up

Development version (MyGet):

Install-Package EnjoyCQRS -Source https://www.myget.org/F/enjoy/api/v3/index.json

Released version (NuGet):

Install-Package EnjoyCQRS

You will need to implement these abstractions:

  • CommandDispatcher
  • IEventRouter
  • IEventStore
  • ITextSerializer
  • ILoggerFactory (you can use the dummy implementation called: NoopLoggerFactory)

You'll need to configure dependencies:

Autofac example:

var builder = new ContainerBuilder();

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<Session>().As<ISession>().InstancePerLifetimeScope();
builder.RegisterType<Repository>().As<IRepository>();
builder.Register(c => new IntervalSnapshotStrategy()).As<ISnapshotStrategy>();
builder.RegisterType<NoopLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
builder.RegisterType<EventPublisher>().As<IEventPublisher>().InstancePerLifetimeScope();
builder.RegisterType<EventSerializer>().As<IEventSerializer>();
builder.RegisterType<SnapshotSerializer>().As<ISnapshotSerializer>();

Register your command handlers:

var genericCommandHandler = typeof(ICommandHandler<>);

builder.RegisterAssemblyTypes(typeof(FooCommandHandler).Assembly)
        .AsClosedTypesOf(genericCommandHandler)
        .AsImplementedInterfaces();

And your event handlers:

var genericEventHandler = typeof(IEventHandler<>);

builder.RegisterAssemblyTypes(typeof(FooEventHandler).Assembly)
        .AsClosedTypesOf(genericEventHandler)
        .AsImplementedInterfaces();

Using Autofac as Event router implementation

public class AutofacEventRouter : IEventRouter
{
    private readonly ILifetimeScope _scope;

    public AutofacEventRouter(ILifetimeScope scope)
    {
        _scope = scope;
    }
    
    public async Task RouteAsync<TEvent>(TEvent @event) 
        where TEvent : IDomainEvent
    {
        var handlers = _scope.ResolveOptional<IEnumerable<IEventHandler<TEvent>>>();

        foreach (var handler in handlers)
        {
            await handler.ExecuteAsync(@event).ConfigureAwait(false);
        }
    }
}

Using Autofac as Command dispatcher implementation

public class AutofacCommandDispatcher : CommandDispatcher
{
    private readonly ILifetimeScope _scope;

    public AutofacCommandDispatcher(ILifetimeScope scope)
    {
        _scope = scope;
    }
    
    protected override async Task RouteAsync<TCommand>(TCommand command)
    {
        var handler = _scope.Resolve<ICommandHandler<TCommand>>();

        await handler.ExecuteAsync(command);
    }
}

Basic

Implementing your Aggregate

public class Person : Aggregate
{
    public string Name { get; private set; }

    public Person() { }

    public Person(Guid id, string name) : this()
    {
        Emit(new PersonCreated(id, name));
    }

    protected override void RegisterEvents()
    {
        SubscribeTo<PersonCreated>(evt =>
        {
            Id = evt.AggregateId;
            Name = evt.Name;
        });

        SubscribeTo<NameChanged>(evt =>
        {
            Name = evt.Name;
        });
    }

    public void ChangeName(string name)
    {
        Emit(new NameChanged(Id, name));
    }
}

Implementing your Event DTO

public class PersonCreated : DomainEvent
    {
        public string Name { get; }

        public PersonCreated(Guid aggregateId, string name) : base(aggregateId)
        {
            Name = name;
        }
    }

Implementing your command DTO

public class CreatePerson : Command
{
    public string Name { get; }

    public CreatePerson(Guid aggregateId, string name) : base(aggregateId) 
    {
        Name = name;
    }
}

Implementing your Command Handler

public class CreatePersonHandler : ICommandHandler<CreatePerson>
{
    private readonly IRepository _repository;

    public CreatePersonHandler(IRepository repository)
    {
        _repository = repository;
    }
    
    public Task ExecuteAsync(CreatePerson command)
    {
        var person = new Person(command.AggregateId, command.Name);

        _repository.AddAsync(person);

        return Task.CompletedTask;
    }
}

Implementing your Event Handler

public class PersonCreatedHandler : IEventHandler<PersonCreated>
{
    public Task ExecuteAsync(PersonCreated @event)
    {
        Console.WriteLine(@event);

        return Task.CompletedTask;
    }
}

Implementing your Text Serializer (using Newtonsoft)

public class JsonTextSerializer : ITextSerializer
{
    private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings
    {
        ContractResolver = new CamelCasePropertyNamesContractResolver()
    };

    public string Serialize(object @object)
    {
        return JsonConvert.SerializeObject(@object, _jsonSerializerSettings);
    }

    public object Deserialize(string textSerialized, string type)
    {
        return JsonConvert.DeserializeObject(textSerialized, Type.GetType(type));
    }

    public T Deserialize<T>(string textSerialized)
    {
        return JsonConvert.DeserializeObject<T>(textSerialized, _jsonSerializerSettings);
    }
}

The full Autofac example looks like:

var builder = new ContainerBuilder();

builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope();
builder.RegisterType<Session>().As<ISession>().InstancePerLifetimeScope();
builder.RegisterType<Repository>().As<IRepository>();
builder.Register(c => new IntervalSnapshotStrategy()).As<ISnapshotStrategy>();
builder.RegisterType<NoopLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
builder.RegisterType<EventPublisher>().As<IEventPublisher>().InstancePerLifetimeScope();
builder.RegisterType<EventSerializer>().As<IEventSerializer>();
builder.RegisterType<SnapshotSerializer>().As<ISnapshotSerializer>();
builder.RegisterType<AutofacEventRouter>().As<IEventRouter>();
builder.RegisterType<AutofacCommandDispatcher>().As<ICommandDispatcher>();
builder.RegisterType<JsonTextSerializer>().As<ITextSerializer>();

// your Event Store implementation
builder.RegisterType<YourEventStore>().As<IEventStore>();

var genericCommandHandler = typeof(ICommandHandler<>);

builder.RegisterAssemblyTypes(typeof(CreatePersonHandler).Assembly)
        .AsClosedTypesOf(genericCommandHandler)
        .AsImplementedInterfaces();
        
var genericEventHandler = typeof(IEventHandler<>);

builder.RegisterAssemblyTypes(typeof(PersonCreatedHandler).Assembly)
        .AsClosedTypesOf(genericEventHandler)
        .AsImplementedInterfaces();

var container = builder.Build();

Feel free to choose your DI Container

Soon I'll improve the setup part. :-)