Super-short example

So how does it actually look when writing an application with Revo?

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.

Event

The event that happens when changing a task's name.

public class TodoRenamedEvent : DomainAggregateEvent
{
    public TodoRenamedEvent(string name)
    {
        Name = name;
    }

    public string Name { get; }
}

Aggregate

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 and command handler

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 projection

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 and query handler

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>());
    }
}

ASP.NET Core controller

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; }
    }
}

Finish!

Now you are ready save the TO-DOs to an event store and read them from regular RDBMS read models.

Last updated