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

Saturday 21 January 2023

C# Functional Project - Unit

Functional programming relies upon the ability to combine functions. This can be achieved in C# using the Func delegate. However, the Func delegate only works for functions that return a value and not void. Void is not a value, and, so a different mechanism is required. C# uses the Action delegate. To create a uniform experience it is useful to introduce a type that indicates a function performs and action/command rather than a query.

Top

In this post...

Top

Command Versus Queries

It is probably fair to say that most software applications consist of commands and queries. As one might expect a command modifies the state of a program. A query, asks for data without modifying the program state.

Top

Commands

Delving a little deeper, a command performs one or more tasks to complete a issued command. Generally, it makes no sense to return a value, (bar error information). Commands include creating/updating database records, writing to a log file, writing to a file and so. In C# a function/method that returns nothing specifies its return "type" as void.

Top

Queries

Where a command does not return a value, a query always returns a value. One would also hope that calling the same function, with the same input value would always yield the same, exact results. As a ridiculous example, consider the following function...

int Add4(int input)
{
  return input + 4;
}

If I call the Add4 function with 6, I would expect a return value of 10. If I call the same function, 6 months later, after perfoming code updates, with the same parameter, 6, I WOULD still expect to receive a value of 10.

Top

The Unit Type

OK, some might be asking. What does any of this have to with a new type, namely Unit. The answer is surprisingly simple. We need functions to return a value so that functions can be combined/chained. Currently, for functions returning void, one must use The Action delegate, for functions returning a value, the Func delegate is used. Trying to combine Actions and Funcs, is possible, but is far from intuitive.

You can return anything as a Unit type. The key is to be consistent. You could, for example, simply return an integer, say zero. To offer more type safety, I declare Unit as follows...

public static partial class Fun
{
  public readonly struct Unit
  {
  }

  public static readonly Unit unit = new Unit();
}
Top

Using the Unit Type

To use the Unit type simply ensure commands that normally return a void now return a unit. Assume I have the following method...

public void UpdateUser(User user)
{
  // perform some action here
}

The above code should be modified to return the Unit type...

public Unit UpdateUser(User user)
{
  // perform some action here
  return unit;
}

Notice the case difference. The function declaration specifies Unit as the return type. The function body returns unit (lowercase), i.e. an instance of the Unit type.

Top Top

Sunday 15 January 2023

Working-With-Byte-Arrays

In this post I will show how to read/write different data type values to and from a byte buffer. This should prove useful when working with block data or pages. In this scenario, pages describes database pages or similar block data mechanisms.


Top

In this post...

Top

How To Handle Typed Values

The aim is to read/write different typed values from a byte array. In C++ one can achieve this by using a union. In C# we achieve this by using a struct/class using explicit layout. Explicit layout is the key here as it ensures types are aligned correctly.

The basic idea is to have a number of byte values offset from zero to maximum bytes required. Then add basic types, such as int 16, int 32, int 64, float, etc all at offset zero. Then one can set a float value and read of the four byte values. The same can be done for most basic types, set the value and the read the corresponding byte values. To read typed values, simply populate the byte values accordingly then read the basic type info (int 16, int 32, int float, etc).

For those with a COM background this approach is similar to the VARIANT type.

Top

The Union Value Type


using System;
using System.Runtime.InteropServices;
namespace Blog
{
  [StructLayout(LayoutKind.Explicit)]
  public struct Value
  {
    [FieldOffset(0)] public byte B0;
    [FieldOffset(1)] public byte B1;
    [FieldOffset(2)] public byte B2;
    [FieldOffset(3)] public byte B3;
    [FieldOffset(4)] public byte B4;
    [FieldOffset(5)] public byte B5;
    [FieldOffset(6)] public byte B6;
    [FieldOffset(7)] public byte B7;

    [FieldOffset(0)] public float Float;
    [FieldOffset(0)] public double Double;
    [FieldOffset(0)] public Int16 Int16;
    [FieldOffset(0)] public Int32 Int32;
    [FieldOffset(0)] public Int64 Int64;
  }
}

The above struct declaration shows eight bytes, B0-B7 for data transfer. If using the decimal type this will need to be expanded to include 16 bytes! As can be seen, B0-B7 is byte aligned and offset accordingly.

Note also, that basic types float, double, Int16, etc all start at offset zero. So, if one sets the Float field, the corresponding 4 bytes can be read from B0-B3. To reverse the action, set fields B0-B3, then read the float value.

It is imperative that one knows how many bytes are required for a typed value. The above structure should cover most data types bar decimal. DateTime values can also be stored by first converting to long and using the Int64 field or bytes B0-B7 to recreate.

Top

Testing The Union Value Type

The above can be tested using the following code...

static void RawTest()
{
  Value value = new Value();
  byte[] data = new byte[128];

  int cursor = 0;
  
  // *********************************************************************************************
  // Writing values to the data byte array.
  // *********************************************************************************************
  Int16 v1 = Int16.MaxValue - 1067;
  Int32 v2 = Int32.MaxValue - 10067;
  float v3 = float.MaxValue - 1.07896f;

  // Write an Int16 value to the data byte array.
  // First set the Value's Int16 field, then read two Value bytes.
  value.Int16 = v1;
  data[cursor++] = value.B0;
  data[cursor++] = value.B1;

  // Write an Int32 value to the data byte array.
  // First set the Value's Int32 field, then read four Value bytes.
  value.Int32 = v2;
  data[cursor++] = value.B0;
  data[cursor++] = value.B1;
  data[cursor++] = value.B2;
  data[cursor++] = value.B3;

  // Write a float value to the data byte array.
  // First set the Value's Float field, then read four Value bytes.
  value.Float = v3;
  data[cursor++] = value.B0;
  data[cursor++] = value.B1;
  data[cursor++] = value.B2;
  data[cursor++] = value.B3;

  
  // *********************************************************************************************
  // Reading values from the data byte array.
  // *********************************************************************************************
  cursor = 0;
  
  // Read an Int16 from the data byte array.
  // Set Value's first two byte fields, then read the Value's Int16 field.
  value.B0 = data[cursor++];
  value.B1 = data[cursor++];
  var v1Result = value.Int16;

  // Read an Int32 from the data byte array.
  // Set Value's first four byte fields, then read the Value's Int32 field.
  value.B0 = data[cursor++];
  value.B1 = data[cursor++];
  value.B2 = data[cursor++];
  value.B3 = data[cursor++];
  var v2Result = value.Int32;

  // Read a float from the data byte array.
  // Set Value's first four byte fields, then read the Value's Float field.
  value.B0 = data[cursor++];
  value.B1 = data[cursor++];
  value.B2 = data[cursor++];
  value.B3 = data[cursor++];
  var v3Result = value.Float;

  Console.WriteLine(v1);
  Console.WriteLine(v1Result);
  Console.WriteLine(v2);
  Console.WriteLine(v2Result);
  Console.WriteLine(v3);
  Console.WriteLine(v3Result);
}
Top

Writing Values To A Byte Array

Figure 2 illustrates a byte array filled with values from the previous test code. As the diagram illustrates, data values are byte aligned accordingly. That is, a 16 bit-integer requires 2 bytes, a float or 32-bit integer requires 4 bytes. This approach works well for data blocks comprising of byte arrays. Simply create a byte array, write values and save to disk. Conversely, load a byte array block from disk into memory, then proceed to read actual values.

Top

Improving The Value Interface

The current method of reading/writing values is somewhat verbose. One needs to track the byte array offset (cursor) and the offset to add following a read or write. In addition, explicitly reading and writing to the Value's byte fields (B0...BN) is tiresome and error-prone. The following should help alleviate these problems...

  • Specify the byte array and initial offset.
  • Specify a cursor that is relative to the specified initial offset.
  • Update the cursor accordingly following a read or write operation.
  • Allow the cursor's position to be set manually, the cursor will always be relative to the specified offset.

The following class, ByteBuffer, implements the above features.

using System;

namespace Blog
{
  /// <summary>
  /// Allows values to read/written to/from a byte array.
  /// Specify a byte buffer and initial offset in the constructor.
  /// Data will be read/written at this offset.
  /// The class uses a cursor to indicate current read/write position.
  /// The cursor is always offset by the offset specified in the constructor.
  /// </summary>
  public class ByteBuffer
  {
    private Value _value = new Value();
    private int _internalCursor;
    private int _offset;
    private readonly byte[] _buffer;

    public int Cursor => _internalCursor - _offset;

    public ByteBuffer(byte[] buffer, int offset)
    {
      _buffer = buffer;
      _offset = offset;
      _internalCursor = offset;
    }

    public ByteBuffer SetCursor(int position)
    {
      _internalCursor = _offset + position;
      return this;
    }

    public ByteBuffer Int16(Int16 value)
    {
      _value.Int16 = value;
      _buffer[_internalCursor++] = _value.B0;
      _buffer[_internalCursor++] = _value.B1;
      return this;
    }

    public ByteBuffer Int16(out Int16 result)
    {
      _value.B0 = _buffer[_internalCursor++];
      _value.B1 = _buffer[_internalCursor++];
      result = _value.Int16;
      return this;
    }

    public ByteBuffer Int32(Int32 value)
    {
      _value.Int32 = value;
      _buffer[_internalCursor++] = _value.B0;
      _buffer[_internalCursor++] = _value.B1;
      _buffer[_internalCursor++] = _value.B2;
      _buffer[_internalCursor++] = _value.B3;
      return this;
    }

    public ByteBuffer Int32(out Int32 result)
    {
      _value.B0 = _buffer[_internalCursor++];
      _value.B1 = _buffer[_internalCursor++];
      _value.B2 = _buffer[_internalCursor++];
      _value.B3 = _buffer[_internalCursor++];

      result = _value.Int32;
      return this;
    }

    public ByteBuffer Float(float value)
    {
      _value.Float = value;
      _buffer[_internalCursor++] = _value.B0;
      _buffer[_internalCursor++] = _value.B1;
      _buffer[_internalCursor++] = _value.B2;
      _buffer[_internalCursor++] = _value.B3;
      return this;
    }

    public ByteBuffer Float(out float result)
    {
      _value.B0 = _buffer[_internalCursor++];
      _value.B1 = _buffer[_internalCursor++];
      _value.B2 = _buffer[_internalCursor++];
      _value.B3 = _buffer[_internalCursor++];
      result = _value.Float;
      return this;
    }
  }
}
Top

Using The ByteBuffer Class

Using the ByteBuffer class is fairly straightforward. Simply call the constructor with a byte array and offset. No data can be read or written before the offset.

A sample test/driver program now follows...

static void Main(string[] args)
{
  byte[] data = new byte[128];
  ByteBuffer buffer = new ByteBuffer(data, 5);

  Int16 v1 = Int16.MaxValue - 1067;
  Int32 v2 = Int32.MaxValue - 10067;
  float v3 = float.MaxValue - 1.07896f;

  int bytesWritten = buffer
    .Int16(v1)
    .Int32(v2)
    .Float(v3)
    .Cursor;
  Console.WriteLine($"{bytesWritten} bytes written to buffer.");

  int bytesRead = buffer
    .SetCursor(0)
    .Int16(out var v1Read)
    .Int32(out var v2Read)
    .Float(out var v3Read)
    .Cursor;

  Console.WriteLine($"{bytesRead} bytes read from buffer.");
  Console.WriteLine($"v1Write:{v1} - v1Read:{v1Read}");
  Console.WriteLine($"v2Write:{v2} - v2Read:{v2Read}");
  Console.WriteLine($"v3Write:{v3} - v3Read:{v3Read}");
}

The above code wites the following to the console.

Notice how the test program uses an offset of 5 when constructing the ByteBuffer. The correct bytes read/wrriten of ten is still returned. The first five bytes in this example will be zeroed.

Top