CommandQuery 2.0.0

Command Query Separation for .NET Framework and .NET Standard ⚙️

✔️ Build services that separate the responsibility of commands and queries
✔️ Focus on implementing the handlers for commands and queries
✔️ Create APIs with less boilerplate code
📄 https://hlaueriksson.me/CommandQuery/

Install-Package CommandQuery -Version 2.0.0
dotnet add package CommandQuery --version 2.0.0
<PackageReference Include="CommandQuery" Version="2.0.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add CommandQuery --version 2.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.
#r "nuget: CommandQuery, 2.0.0"
#r directive can be used in F# Interactive, C# scripting and .NET Interactive. Copy this into the interactive tool or source code of the script to reference the package.
// Install CommandQuery as a Cake Addin
#addin nuget:?package=CommandQuery&version=2.0.0

// Install CommandQuery as a Cake Tool
#tool nuget:?package=CommandQuery&version=2.0.0
The NuGet Team does not provide support for this client. Please contact its maintainers for support.

CommandQuery ⚙️

build CodeFactor

Command Query Separation for .NET

Commands

Commands change the state of a system but [traditionally] do not return a value. They write (create, update, delete) data.

Commands implement the marker interface ICommand and command handlers implement ICommandHandler<in TCommand>.

public class FooCommand : ICommand
{
    public string Value { get; set; }
}

public class FooCommandHandler : ICommandHandler<FooCommand>
{
    private readonly ICultureService _cultureService;

    public FooCommandHandler(ICultureService cultureService)
    {
        _cultureService = cultureService;
    }

    public async Task HandleAsync(FooCommand command, CancellationToken cancellationToken)
    {
        if (command.Value == null) throw new FooCommandException("Value cannot be null", 1337, "Try setting the value to 'en-US'");

        _cultureService.SetCurrentCulture(command.Value);

        await Task.CompletedTask;
    }
}

Commands can also return a result.

public class BazCommand : ICommand<Baz>
{
    public string Value { get; set; }
}

public class Baz
{
    public bool Success { get; set; }
}

public class BazCommandHandler : ICommandHandler<BazCommand, Baz>
{
    private readonly ICultureService _cultureService;

    public BazCommandHandler(ICultureService cultureService)
    {
        _cultureService = cultureService;
    }

    public async Task<Baz> HandleAsync(BazCommand command, CancellationToken cancellationToken)
    {
        var result = new Baz();

        try
        {
            _cultureService.SetCurrentCulture(command.Value);

            result.Success = true;
        }
        catch
        {
            // TODO: log
        }

        return await Task.FromResult(result);
    }
}

Commands with result implement the marker interface ICommand<TResult> and command handlers implement ICommandHandler<in TCommand, TResult>.

Queries

Queries return a result and do not change the observable state of the system (are free of side effects). They read and return data.

Queries implement the marker interface IQuery<TResult> and query handlers implement IQueryHandler<in TQuery, TResult>.

public class BarQuery : IQuery<Bar>
{
    public int Id { get; set; }
}

public class Bar
{
    public int Id { get; set; }

    public string Value { get; set; }
}

public class BarQueryHandler : IQueryHandler<BarQuery, Bar>
{
    private readonly IDateTimeProxy _dateTime;

    public BarQueryHandler(IDateTimeProxy dateTime)
    {
        _dateTime = dateTime;
    }

    public async Task<Bar> HandleAsync(BarQuery query, CancellationToken cancellationToken)
    {
        var result = new Bar { Id = query.Id, Value = _dateTime.Now.ToString("F") };

        return await Task.FromResult(result);
    }
}
Samples

CommandQuery ⚙️

build CodeFactor

Command Query Separation for .NET

Commands

Commands change the state of a system but [traditionally] do not return a value. They write (create, update, delete) data.

Commands implement the marker interface ICommand and command handlers implement ICommandHandler<in TCommand>.

public class FooCommand : ICommand
{
    public string Value { get; set; }
}

public class FooCommandHandler : ICommandHandler<FooCommand>
{
    private readonly ICultureService _cultureService;

    public FooCommandHandler(ICultureService cultureService)
    {
        _cultureService = cultureService;
    }

    public async Task HandleAsync(FooCommand command, CancellationToken cancellationToken)
    {
        if (command.Value == null) throw new FooCommandException("Value cannot be null", 1337, "Try setting the value to 'en-US'");

        _cultureService.SetCurrentCulture(command.Value);

        await Task.CompletedTask;
    }
}

Commands can also return a result.

public class BazCommand : ICommand<Baz>
{
    public string Value { get; set; }
}

public class Baz
{
    public bool Success { get; set; }
}

public class BazCommandHandler : ICommandHandler<BazCommand, Baz>
{
    private readonly ICultureService _cultureService;

    public BazCommandHandler(ICultureService cultureService)
    {
        _cultureService = cultureService;
    }

    public async Task<Baz> HandleAsync(BazCommand command, CancellationToken cancellationToken)
    {
        var result = new Baz();

        try
        {
            _cultureService.SetCurrentCulture(command.Value);

            result.Success = true;
        }
        catch
        {
            // TODO: log
        }

        return await Task.FromResult(result);
    }
}

Commands with result implement the marker interface ICommand<TResult> and command handlers implement ICommandHandler<in TCommand, TResult>.

Queries

Queries return a result and do not change the observable state of the system (are free of side effects). They read and return data.

Queries implement the marker interface IQuery<TResult> and query handlers implement IQueryHandler<in TQuery, TResult>.

public class BarQuery : IQuery<Bar>
{
    public int Id { get; set; }
}

public class Bar
{
    public int Id { get; set; }

    public string Value { get; set; }
}

public class BarQueryHandler : IQueryHandler<BarQuery, Bar>
{
    private readonly IDateTimeProxy _dateTime;

    public BarQueryHandler(IDateTimeProxy dateTime)
    {
        _dateTime = dateTime;
    }

    public async Task<Bar> HandleAsync(BarQuery query, CancellationToken cancellationToken)
    {
        var result = new Bar { Id = query.Id, Value = _dateTime.Now.ToString("F") };

        return await Task.FromResult(result);
    }
}
Samples

Release Notes

- Removed target framework net461 🎯
- Renamed method ProcessWithResultAsync to ProcessAsync in CommandProcessor 💥
- Added CancellationToken parameter to ProcessAsync methods in CommandProcessor and QueryProcessor
- Renamed CommandTypeCollection to CommandTypeProvider 💥
- Renamed QueryTypeCollection to QueryTypeProvider 💥
- Added method AssertConfigurationIsValid to CommandProcessor and QueryProcessor
- CommandProcessor, CommandTypeProvider, QueryProcessor and QueryTypeProvider are now added to IServiceCollection as singletons
- CommandTypeException is now thrown if multiple commands with the same name is added to the CommandTypeProvider
- QueryTypeException is now thrown if multiple queries with the same name is added to the QueryTypeProvider

NuGet packages (9)

Showing the top 5 NuGet packages that depend on CommandQuery:

Package Downloads
CommandQuery.AspNetCore
Command Query Separation for ASP.NET Core 🌐 ✔️ Provides generic actions for handling the execution of commands and queries ✔️ Enables APIs based on HTTP POST and GET 📄 https://hlaueriksson.me/CommandQuery.AspNetCore/
CommandQuery.AzureFunctions
Command Query Separation for Azure Functions ⚡ ✔️ Provides generic function support for commands and queries with HTTPTriggers ✔️ Enables APIs based on HTTP POST and GET 📄 https://hlaueriksson.me/CommandQuery.AzureFunctions/
CommandQuery.AWSLambda
Command Query Separation for AWS Lambda ⚡ ✔️ Provides generic function support for commands and queries with Amazon API Gateway ✔️ Enables APIs based on HTTP POST and GET 📄 https://hlaueriksson.me/CommandQuery.AWSLambda/
CommandQuery.AspNet.WebApi
Command Query Separation for ASP.NET Web API 2 🌐 ✔️ Provides generic actions for handling the execution of commands and queries ✔️ Enables APIs based on HTTP POST and GET 📄 https://hlaueriksson.me/CommandQuery.AspNet.WebApi/
CommandQuery.DependencyInjection
Dependency Injection for CommandQuery

GitHub repositories

This package is not used by any popular GitHub repositories.

Version History

Version Downloads Last updated
2.0.0 147 7/29/2021
1.0.0 2,598 2/2/2020
0.9.0 1,686 11/20/2019
0.8.0 4,079 2/16/2019
0.7.0 866 9/22/2018
0.6.0 759 9/15/2018
0.5.0 815 7/6/2018
0.4.0 798 5/16/2018
0.3.2 818 5/1/2018
0.3.1 864 1/6/2018
0.3.0 835 1/3/2018
0.2.0 938 4/25/2017
0.1.1 1,012 8/29/2016
0.1.0 841 8/28/2016