Saturday, 28 January 2023

CQRS - C# Style

CQRS - Command and Query Responsibility Segregation.

Quite a mouthful, but essentially is the art of seperating commands and queries with an application. A command chages state, a query retruns data and does not change state.

Most CQRS libraries that I have encountered are verbose and have a large footprint in terms of memory and disk space

I have developed a simple but usuable CQRS library. Currently the library caters for commands, queries are usually much simpler to implement.


Top

In this post...

Top

Commands

A command performs some kind of action and is expected to modify application state. Examples include writing to a database, the console, painting a control. A command typically raises an event to inform the application of state changes. A command will usually contain parameters, used to modify application state. A seperate class is typically responsible for running commands. This allows pre and post actions when acting upon commands.

Top

Events

Commands, as stated perform state changes. An application is informed of state changes using events. In my opinion it is best to keep event classes seperate from commands. This promotes loose-coupling. Consider C# control events, the control hosts the event handler. This means anything wishing to respond to an event must access the control to add the event. This results in tight coupling.

Top

CQRS library

Currently, my CQRS library, whilst simple only caters forcommand end events.

It is available as a separate project to include where requied.

The code follows, then an analysis follows...

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

public static class CQRS
{
  public static readonly CommandBuilder Commands = new CommandBuilder();

  public interface IMessage { }

  public class Event : IMessage { }

  public class Command : IMessage { }

  public interface IHandleCommand<T>
    where T : Command
  {
    Task Handle(T command);
  }

  public class CommandBuilder
  {
    public CommandBuilder Handle<T>(IHandleCommand<T> handler)
      where T : Command
    {
      CommandProcessor<T>.Handler(handler);
      return this;
    }
  }

  public static void Send<T>(T command)
    where T : Command =>
    CommandProcessor<T>.Process(command);

  public static void Raise<T>(T @event)
    where T : Event =>
    EventBus<T>.Raise(@event);

  public static void EventSubscribe<T>(Action<T> handler)
    where T : Event =>
    EventBus<T>.Subscribe(handler);

  public static void EventUnsubscribe<T>(Action<T> handler)
    where T : Event =>
    EventBus<T>.Unsubscribe(handler);

  static class CommandProcessor<T>
    where T : Command
  {
    private static IHandleCommand<T> _handler;

    public static void Handler(IHandleCommand<T> handler) =>
      _handler = handler;

    public static void Process(T command)
    {
      _handler.Handle(command);
    }
  }

  static class EventBus<T>
    where T : Event
  {
    private static readonly List<Action<l;T>> _handlers = new List<Action<T>&t;();

    public static void Raise(T @event)
    {
      foreach (var handler in _handlers)
        handler(@event);
    }

    public static void Subscribe(Action<T> handler)
    {
      _handlers.Remove(handler);
      _handlers.Add(handler);
    }

    public static void Unsubscribe(Action<T> handler)
    {
      _handlers.Remove(handler);
    }
  }
}
Top

CQRS Library Analysis

The library is a .NET framework library targeted for .NET Framework 4.8. That said, the library is very simple so earlier versions might work.

Commands and events are simply messages, but, with different semantics. So, I create a message interface, IMessage, from which both commands and events will implement. Commands must derive from the Command class. Events must derive from the Event class.

Creating a command is deemed a separate concern to executing a command. That is, a command is created with state that conveys potential application state changes. However, a command is not responsible for running itself, mainly due to a lack of global context.

Running commands within a single entry point aids debugging and allows logging, tracing to be added with relative ease.

For each new Command class created a corresponding command handler needs to be present. The interface, IHandleCommand<T> helps to achieve this.

The idea is that many commands might be available, but, an application may only wish to use a subset of said commands.

Top

CQRS Library Functions

  • Send<T>(T command)
    Executes a command using the private CommandProcessor class. The command parameter must be a type derivedfrom the Command class.
  • Raise<T>(T @event)
    Raises an event, the event must be derived from the Event class.
  • EventSubscribe<T>(Action<T> handler)
    Used to register an event handler. The type parameter, T, must be derived from the Event class.
Top

Sunday, 22 January 2023

Functional Programming - Error Type

In my functional library post, Functional Library Project, I mentioned the error type. The error type is a simple type that allows one to convery errors throughout the system.

Using errors versus exceptions is not an easy choice. Exceptions yield a call stack, useful for debugging. Errors do not yield a call stack. Still, with enough context information the error type is useful.

Exceptions are similar to using a goto statement. Normal program flow is lost and the exception must be handled higher upon the call stack.

Conversely, errors are returned by class methods/functions. Calling functions can catch the error and take appropriate action. The same can also be said about exceptions.

See my post, exceptions versus errors. for an insight into exception/error usage.


Top

In this post...

Top

Error Type

The error type is simply a class that maintains an error message. The constructor is protected as the idea is to create custom errors. The code is as follows...

public static partial class Fun
{
  public class Error
  {
    public string Message { get; }

    protected Error(string message)
    {
      this.Message = message;
    }

    public override string ToString() =>
      $"{this.Message}";
  }
}

Note, the class has no namespace and falls under the partial Fun class. This ensures that functional library code can be included by simply using the following include...

using static Fun
Top

Declare A New Error Type

Error types can be declared as nested classes or within the global (namespace) area. Personally, I prefer to make error classes (and exceptions generally) within the class. The stack trace picks up on this and can make debugging much easier. The following code illustrates the idea for a nested error type.

using static Fun;

namespace TestApp.Domain
{
  public class Customer
  {
    public class EmptyError : Error
    {
      public EmptyError(string varName) : base($"{varName} cannot be empty.") { }
    }

    public string FirstName { get; }
    public string LastName { get; }

    public static (Customer Customer, Error error) Create(string firstName, string lastName)
    {
      if (string.IsNullOrEmpty(firstName))
        return (null, new EmptyError("First Name"));

      if (string.IsNullOrEmpty(lastName))
        return (null, new EmptyError("Last Name"));
      return (new Customer(firstName, lastName), null);
    }


    private Customer(string firstName, string lastName)
    {
      this.FirstName = firstName;
      this.LastName = lastName;
    }
  }
}

Note the private constructor. The only way to create a new customer is to use the static Create method. The only downside is that the caller might ignore the error. In subsequent posts I will improve upon this by using Monads to ensure the caller must respond to errors.

Top

Improved Error Type Usage

In the last section, declare error type, I detailed how one might use the Error class to help signify incorrect class creation. The code allows callers to ignore the error, not great, so a more robust soluition is required.

The following code introduces a Customer result class. A modified customer class uses the new customer result class.

using System;
using static Fun;
namespace TestApp.Domain
{
  public class CustomerResult
  {
    private readonly Error _error;
    private readonly Customer _customer;
    
    public static implicit operator CustomerResult(Customer customer) =>
      new CustomerResult(customer);
      
    public static implicit operator CustomerResult(Error error) =>
      new CustomerResult(null, error);
      
    public CustomerResult(Customer customer, Error error = null)
    {
      _error = error;
      _customer = customer;
    }
      
    public R Match<R>(Func<Error, R> onError, Func<Customer, R> onOK) =>
      null == _error ? onOK(_customer) : onError(_error);
  }
}
Top

The revised customer code follows...

using static Fun;
namespace TestApp.Domain
{
  public class Customer
  {
    public class EmptyError : Error
    {
      public EmptyError(string varName) : base($"{varName} cannot be empty.") { }
    }    
    
    //public static (Customer Customer, Error error) Create(string firstName, string lastName)
    //{
    //  if (string.IsNullOrEmpty(firstName))
    //    return (null, new EmptyError("First Name"));
    //  if (string.IsNullOrEmpty(lastName))
    //   return (null, new EmptyError("Last Name"));
    // return (new Customer(firstName, lastName), null);
    //}
    
    public string FirstName { get; }
    public string LastName { get; } 	
  
    public static CustomerResult Create(string firstName, string lastName)
    {
      if (string.IsNullOrEmpty(firstName))
        return new EmptyError("First Name");
      if (string.IsNullOrEmpty(lastName))
        return new EmptyError("Last Name");
      return new Customer(firstName, lastName);
    }
  
    private Customer(string firstName, string lastName)
    {
      this.FirstName = firstName;
      this.LastName = lastName;
    }
  }
}
Top

Testing Error Type

Using the above code, we can try a fail and a pass scenario. Is always wise to test code, even if you are 100% sure it will work anyway. Before writing test code, it is useful to override some ToString methods.

// Add the following to the customer class
public override string ToString() =>
  $"{LastName}, {FirstName}";
  
// Add the following to the customer result class
public override string ToString() =>
  null == _error ? $"{_customer}" : $"{_error}";

Finally, the test/driver program can be coded. My tests include trying the following...

  • Creating a customer first name with a NULL value.
  • Creating a customer last name with a NULL value.
  • Creating a customer with a VALID first and last name.

The test code is as follows...

using System;
using TestApp.Domain;

namespace TestApp
{
  internal class Program
  {
    static void Main(string[] args)
    {
      var c1 = Customer
        .Create(null, "Bloggs");
      Console.WriteLine(c1);

      var c2 = Customer
        .Create("Fred", null);
      Console.WriteLine(c2);

      var c3 = Customer
        .Create("Fred", "Bloggs");
      Console.WriteLine(c3);
    }
  }
}

The following results should be presented by the console.

Top