These few following paragraphs show how could one design super-simple application that can save tasks using event-sourced aggregates and then query them back from a RDBMS.
A little-bit more complete application based on this code, along with some walkthrough, can be found in the Task list app example.
The event that happens when changing a task's name.
public class TodoRenamedEvent : DomainAggregateEvent{public TodoRenamedEvent(string name){Name = name;}​public string Name { get; }}
The task aggregate root.
public class Todo : EventSourcedAggregateRoot{public Todo(Guid id, string name) : base(id){Rename(name);}protectedTodo(Guid id) : base(id){}​public string Name { get; private set; }​public void Rename(string name){if (!Name != name){Publish(new TodoRenamedEvent(name));}}private void Apply(TodoRenamedEvent ev){Name = ev.Name;}}
Command to save a new task.
public class CreateTodoCommand : ICommand{public CreateTodoCommand(string name){Name = name;}​[Required]public string Name { get; }}
public class TodoCommandHandler : ICommandHandler<CreateTodoCommand>{private readonly IRepository repository;public TodoCommandHandler(IRepository repository){this.repository = repository;}​public Task HandleAsync(CreateTodoCommand command, CancellationToken cancellationToken){var todo = new Todo(command.Id);todo.Rename(command.Name);repository.Add(todoList);return Task.CompletedTask;}}
Read model and a projection for the event-sourced aggregate.
public class TodoReadModel : EntityReadModel{public string Name { get; set; }}
public class TodoListReadModelProjector : EFCoreEntityEventToPocoProjector<Todo, TodoReadModel>{public TodoListReadModelProjector(IEFCoreCrudRepository repository) : base(repository){}​private void Apply(IEventMessage<TodoRenamedEvent> ev){Target.Name = ev.Event.Name;}}
Query to read the tasks back from a RDBMS.
public class GetTodosQuery : IQuery<IQueryable<TodoReadModel>>{}
public class TaskQueryHandler : IQueryHandler<GetTodoQuery, IQueryable<TodoReadModel>>{private readonly IReadRepository readRepository;​public TaskListQueryHandler(IReadRepository readRepository){this.readRepository = readRepository;}​public Task<IQueryable<TodoReadModel>> HandleAsync(GetTodoListsQuery query, CancellationToken cancellationToken){return Task.FromResult(readRepository.FindAll<TodoListReadModel>());}}
Or just any arbitrary endpoint from where to send the command and queries from. :)
[Route("todos")]public class TodoController : CommandApiController{[HttpGet("")]public Task<IQueryable<TodoReadModel>> Get(){return CommandBus.SendAsync(new GetTodosQuery());}​[HttpPost("")]public Task Post([FromBody] CreateTodoDto payload){return CommandBus.SendAsync(new CreateTodoCommand(payload.Name));}public class CreateTodoDto{public string Name { get; set; }}}
Now you are ready save the TO-DOs to an event store and read them from regular RDBMS read models.