Becoming a software developer – episode XIII

Becoming a software developer – episode XIII

Welcome to the thirteenth episode of my course “Becoming a software developer” in which we will make use of the Command Handler pattern in order to extend our business logic and clean up the controllers.

All of the materials including videos and sample projects can be downloaded from here.
The source code repository is being hosted on GitHub.


 

Scope

  • Command Handler
  • Command Dispatcher

Abstract

Command Handler

We can encapsulate our business logic even more by using the ICommand interface which is a common way to start using the CQS (Command & Query separation) pattern in our application.
The interface per se is merely a “marker” and can be defined as follows:

Having ICommand in place, we can define as many commands as we want to:

Eventually, we can add define the generic ICommandHandler interface:

And implement the handler for our commands, like this:

Here comes the question – why would you even bother to do that? Usually, the business logic consists more than a single operation to be invoked in order to complete its flow.
Command handlers are a great way to achieve such goal, as we’re able to inject as many services as we need to. Otherwise, we would have to create a separate interfaces dedicated for a single business logic unit, which would do exactly the same (or in the worst case scenario, write such code within our controllers that should be as transparent as possible).

Command Dispatcher

Now, how can we enforce our software to automatically wire up commands to the particular command handlers? And even more importantly, how can we order our controllers, to resolve the proper command handler? At first, let’s define the ICommandDispatcher interface along with its implementation:

Notice that we’re making use of IComponentContext which is a part of the AutofacAutofac IoC container.
Next step, is to register all of the commands and the appropriate command handlers:

And finally, we can inject the dispatcher into our controller and redefine our HTTP requets to look as simple as that:

Next

In the next episode, you’ll find out how to configure the application by passing the configuration classes that can be defined and mapped from the appsettings.json.
Moreover, you shall see my efforts in trying to find out why something didn’t work as expected in the first place ;).

11 Comments Becoming a software developer – episode XIII

  1. Pingback: Dew Drop - April 20, 2017 (#2462) - Morning Dew

  2. Matt

    Can you add some additional article on blog maybe about global decorator to all handlers and decorators that works only with specific handler ?

    Reply
      1. Matt

        For example when we have CreateUserHandler. And on that handler we are creating our user, connect this user with other objects in the system. But for this example i would like to have CreateUserComandValidator (thats our decorator) and then i have CreateUserHandler that is executed only when CreateUserComandValidator checks all the data and confirms they are correct.

        On more complex objects it scales much better. And still on CommandDispatcher we are giving only our CreateUserComand.

        And on global decorator to all handlers, for example we could Log informations what handlers are executing with what kind of data.

        Reply
  3. Arek

    W jaki sposób do projektu byś zaczął implementować frontend np. Angular4 od strony architektury ? Nowy projekt typu Passager.WebSite czy w samym Api i komunikacja między 2 stronami? Mógłbyś troszkę nakierować jak by można to prawidłowo wykonać ?

    Reply
  4. Tk

    Mam takie pytanie, bo próbowałem zrobić na wzór tego handlera własnego i w Create metodzie w api przyjmuje obiekt CreateCategory, w którym mam tylko właściwość Category potrzebny w repo do stworzenia nowej Category tylko nie wiem jak zdefiniować jsona teraz lub co ma przyjmować jako command metoda w api odpowiedzialna za tworzenie nowej kategorii?

    Reply
    1. Tk

      a chyba sobie sam odpowiedziałem na pytanie, ale jakby ktoś miał problem to trzeba uwtorzyć nowy obiekt i w nim definiować nazwy pól 🙂 niby takie łatwe…

      Reply
  5. ziko

    Have you already tried this approach for modules registration on .net Core 2.0? Configuration of Startup.cs is quite different.
    I’m using Autofac 4.6.2 & Autofac.Extensions.DependencyInjection 4.2.0 having web api in Core 2.0.
    Basic registration works as expected with registering by types of interfaces using Autofac but when it comes to modules it doesn’t , following your example.
    Any suggestion?

    Reply
  6. paweł

    Chcę dodatkowo dostać w kontrolerze jakąś odpowiedź.
    Robię POST-a ale przekazywany obiekt w repozytorium trafia do funkcji wykonywanej przez bazę danych. Funkcja zwraca obiekt JSON. I ten obiekt chciałem ‘przepchnąć’ z powrotem aż do kontrolera – może być w nim np opis błędu…

    Bazując na Twoim projekcie dokonałem małych modyfikacji:

    //interfejs handler-a:
    public interface ICommandHandler where T : ICommand where R : class, new()
    {
    Task HandleAsync(T command) ;
    }

    //dispatcher:
    public interface ICommandDispatcher
    {
    Task DispatchAsync(T command) where T : ICommand;

    Task DispatchAsync(T command) where T : ICommand where R : class, new();
    }

    public async Task DispatchAsync(T command) where T : ICommand where R :class, new()
    {
    if (command == null)
    throw new ModelException(ErrorCodes.ErrorCode.WrongCommand, $”Command: ‘{typeof(T).Name}’ cannot be null.”);

    var handler = _context.Resolve<ICommandHandler>();
    return await handler.HandleAsync(command);
    }

    //rejestracja w IoC:
    builder.RegisterAssemblyTypes(assembly)
    .AsClosedTypesOf(typeof(ICommandHandler))
    .InstancePerLifetimeScope();

    //wywołanie w kontrolerze:
    var res = await Dispatch2Async(command);
    return res;

    i to na czym poległem – implementacja handlera dla komendy:

    public class ObjectSetHandler : ICommandHandler
    {
    private readonly IObjectService _service;

    public ObjectSetHandler(IObjectService service)
    {
    _service = service;
    }

    //VS generuje interfejs tak:
    public Task HandleAsync(T command)
    {
    throw new System.NotImplementedException();
    }
    //a chyba powinno być tak.
    public async Task HandleAsync(CreateObject command)
    => await _service.AddObjectAsync(command.Name, command.value);
    }

    Co ciekawe kod się kompiluje, i do automatycznie wygenerowanej metody HandleAsync dociera prawidłowy obiekt command, niestety nie mogę ko konwertować T > CreateObject, podobnie rezultatu R > ReturnObject.

    Gdzie popełniam błąd?

    Reply
    1. paweł

      upały 🙁
      źle wkleiłem końcówkę – znzczy błąd w pytaniu bo odpowiedzi dalej nie znam 🙁

      public class ObjectSetHandler : ICommandHandler
      {
      private readonly IObjectService _service;

      public ObjectSetHandler(IObjectService service)
      {
      _service = service;
      }

      //VS generuje interfejs tak:
      public Task HandleAsync(T command)
      {
      throw new System.NotImplementedException();
      }

      //a chyba powinno być tak.
      public Task HandleAsync(CreateObject command)
      => await _service.AddObjectAsync(command.Name, command.value);
      }

      Reply

Leave a Reply to Tk Cancel reply

Your email address will not be published. Required fields are marked *