Patterns & Abstractions

Patterns and Abstractions

My central architectural pattern of choice is CQRS.


Commands

Actions that change something and return nothing

public interface ICommandHandler<TCommand> where TCommand : ICommand
{
    void Run(TCommand command);
}

Queries

Actions that return something and change nothing

public interface IQuery<TResult>
{
}

public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
    TResult Execute(TQuery query);
}

Mediators

A combination of commands and queries

There are two mediator abstractions. The first mediates calls to commands and handlers and ultimately returns nothing

public interface IBusinessTransaction
{
}

public interface IBusinessTransactionHandler<TRequest> where TRequest : IBusinessTransaction
{
    void Negotiate(TRequest request);
}

The second mediates calls to commands and handlers and ultimately returns something

public interface IBusinessTransaction<TResponse> : IBusinessTransaction
{
}

public interface IBusinessTransactionHandler<TRequest, TResponse> where TRequest : IBusinessTransaction<TResponse>
{
    TResponse Negotiate(TRequest request);
}

Events (Observers)

Side effects

public interface IEvent
{
}

public interface ISubscriber<TEvent> where TEvent : IEvent
{
    void Handle(TEvent param);
}

Here are some example events: here we have 3 generic events to cater for pre and post an activity. These events are designed to be triggered before and after calling each QueryHandler<,> and CommandHandler<>.

public sealed class OnBefore<TRequest> : IEvent
{
    public OnBefore(TRequest response)
    {
        this.Request = response;
    }

    public TRequest Request { get; private set; }
}

public sealed class OnAfter<TRequest> : IEvent
{
    public OnAfter(TRequest request)
    {
        this.Request = request;
    }

    public TRequest Request { get; private set; }
}

public sealed class OnAfter<TRequest, TResponse> : IEvent
{
    public OnAfter(TRequest request, TResponse response)
    {
        this.Request = request;

        this.Response = response;
    }

    public TRequest Request { get; private set; }

    public TResponse Response { get; private set; }
}

Decorators

Cross cutting concerns

Decorators can be logically divided into 2 groups.

Specific cross cutting concerns (A Strategy)

Decorators that affect only one thing. Rather than open up the existing fully unit tested code (in doing so we would break the S and the O in the SOLID principles) we wrap existing code with a decorator.

General cross cutting concerns (Aspect Oriented Programming)

Code that should be applied to all (or the majority of) a specific service type. User activity logging is an example of this. All commands (and queries) can be wrapped with a generic decorator that records certain details on the requested action.

Leave a Reply