Hi! At the beginning I want to point out that this is the first article I publish using English. I’ve decided to diversify my blog posts by using this more commonly used language from time to time. Cutting this long opening I have to mention, that the site you visit is called in Polish „cesarstwo-dev”, what can be translated to „dev-empire”. This article describes the naming convention I use in projects where CQRS is applied. After creating a couple of projects we’ve finally reached the convention which works great for my team. Let’s try to point out how it looks.

1. Projects naming convention

In this article I assume that read and write sites of application are in a common project, but the same convention applies to separation of these concerns. As the example I prepared an application managing Praetorian guards (as in empire). Just like CQRS separates commands and queries, we decided to separate query/command definition from its implementation. As a result, we have a project with postfix „Contracts” which contains requests definitions. This project creates also public interface of the module.

2. Contract definition

Let’s look at the sample definition of query. Firstly, we have to place it somewhere in the project. We decided to create a separate directory for each aggregate (entry type or even another segregation – sometimes we even skip this level). Inside a specific directory we create subdirectories for commands, queries and integration events. Here is an example!

Moving to code of query definition.

    public static class GetUnit
    {
        public class Query : IQuery<UnitDetails>
        {
            public Query(int id)
            {
                Id = id;
            }

            public int Id { get; }
        }

        public class UnitDetails
        {
            public UnitDetails(int id, string name)
            {
                Id = id;
                Name = name;
            }

            public int Id { get; }
            public string Name { get; }
        }
    }

As can be seen, each contract definition is a static class containing a query itself (called Query – always) and response. The response is included only if it’s not some common class using by multiple commands or queries.

3. Handler implementation

In a project containing implementations of queries we can find code looking like this.

 using Contract = Pretorians.Contracts.Units.Queries.GetUnit;
 namespace Praetorians.Units.Queries.GetUnit
 {
     internal class Handler : IQueryHandler<Contract.Query, Contract.UnitDetails>
     {
         public Task Execute(Contract.Query query)
             => Task.FromResult(new Contract.UnitDetails(1, "Josh"));
     }
 }

At first glance you may notice the using statement. Thanks to that we have every query handler implementing interface IQueryHandler<Contract.Query, …>. It also looks clear in my opinion. Then we have an internal class called Handler. In our naming convention every handler is just called Handler. It gave us great consistency and took from us the easy possibility to use specific handler implementation at any place. Who would want to look for specific namespace to find the correct Handler? 🙂 Registration of handlers in DI container is automatic, so even then we do not need accessing specific handlers.

3. Other components

Sometimes we build our mediator pipeline with additional steps. It can be audit saving, metrics, logging or validation. In this naming convention we can create a validator (or any other needed component) for each request and put it next to the corresponding handler. Firstly, take a look at validator code, which is surprisingly similar to handler one.

 using Contract = Pretorians.Contracts.Units.Queries.GetUnit;
 namespace Praetorians.Units.Queries.GetUnit
 {
     internal class Validator : IValidator<Contract.Query, Contract.UnitDetails>
     {
         public Task Validate(Contract.Query query)
             => Task.FromResult(ValidationResult.Success());
     }
 }

Let’s also look at overall solution from higher perspective.

This approach results in grouping connected classes with namespaces what ease searching and analyzing code. Sometimes, when a project which contains handlers is responsible also for handling infrastructure or something else, then shown files can be put in a directory called „Contract Implementation” or „Features”.

4. Usage in controllers

Let’s have a look how to use this naming convention inside a controller using mediatR.

[HttpGet("{id}")]
[ProducesResponseType(typeof(GetUnit.UnitDetails), StatusCodes.Status200OK)]
public async Task GetUnitDetails(
    [FromRoute] int id,
    [FromServices] ISender sender)
{
    var result = await sender.Send(new GetUnit.Query(id));
    return Ok(result);
}

And that’s it! Thanks for your time and do not hesitate to share your opinion about this convention in comments. I’m constantly trying to improve anything I can. And if you speak Polish than you’re more than welcome to review my other blog posts, maybe something about CQRS on one database?


1 Komentarz

dotnetomaniak.pl · 2021-04-28 o 09:29

Naming convention I use for CQRS in C# – Cesarstwo Dev

Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *