Skip to content

Commit

Permalink
Merge pull request #20 from allegro/feature/remove-command-id
Browse files Browse the repository at this point in the history
feat: replace interfaces for query and command with abstract records
  • Loading branch information
rafalschmidt97 authored Jan 27, 2023
2 parents dc773c8 + 6ca90b9 commit c690161
Show file tree
Hide file tree
Showing 21 changed files with 74 additions and 82 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;

namespace Allegro.Extensions.Cqrs.Abstractions.Commands;

/// <summary>
/// Cqrs base representation of command.
/// </summary>
public abstract record Command
{
/// <summary>
/// Command identifier
/// </summary>
public string Id { get; } = Guid.NewGuid().ToString();
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,5 @@ public interface ICommandDispatcher
/// <summary>
/// Sends command to dispatcher
/// </summary>
Task Send<TCommand>(TCommand command) where TCommand : ICommand;
Task Send<TCommand>(TCommand command) where TCommand : Command;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Allegro.Extensions.Cqrs.Abstractions.Commands;
/// <summary>
/// Marker interface to add handler for command. In most cases changes state.
/// </summary>
public interface ICommandHandler<in TCommand> where TCommand : ICommand
public interface ICommandHandler<in TCommand> where TCommand : Command
{
/// <summary>
/// Handles command request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Allegro.Extensions.Cqrs.Abstractions.Commands;
/// Marker interface that allows to execute more complicated validations before action execution.
/// </summary>
/// <typeparam name="T">Supported command type</typeparam>
public interface ICommandValidator<T> where T : ICommand
public interface ICommandValidator<T> where T : Command
{
/// <summary>
/// Validates command before execution. Should throw ValidationException or other exception.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public interface IQueryDispatcher
/// </summary>
/// <typeparam name="TResult">Type of data returned by query</typeparam>
/// <returns>Query data</returns>
Task<TResult> Query<TResult>(IQuery<TResult> query, CancellationToken cancellationToken);
Task<TResult> Query<TResult>(Query<TResult> query, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Allegro.Extensions.Cqrs.Abstractions.Queries;
/// </summary>
/// <typeparam name="TQuery">Type of supported query</typeparam>
/// <typeparam name="TResult">Type of data returned by query</typeparam>
public interface IQueryHandler<in TQuery, TResult> where TQuery : IQuery<TResult>
public interface IQueryHandler<in TQuery, TResult> where TQuery : Query<TResult>
{
/// <summary>
/// Handles query execution. In most cases reading data from read-model directly.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Allegro.Extensions.Cqrs.Abstractions.Queries;
/// Marker interface that allows to execute more complicated validations before action execution.
/// </summary>
/// <typeparam name="T">Supported query type</typeparam>
public interface IQueryValidator<in T> where T : IQuery
public interface IQueryValidator<in T> where T : Query
{
/// <summary>
/// Validates query before execution. Should throw ValidationException or other exception.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;

namespace Allegro.Extensions.Cqrs.Abstractions.Queries;

/// <summary>
/// Cqrs base representation of query.
/// </summary>
public abstract record Query
{
/// <summary>
/// Query identifier
/// </summary>
public string Id { get; } = Guid.NewGuid().ToString();
}

/// <summary>
/// Cqrs base representation of query.
/// </summary>
/// <typeparam name="T">Type of data returned by query</typeparam>
public abstract record Query<T> : Query;
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using System.Threading.Tasks;
using Allegro.Extensions.Cqrs.Abstractions;
using Allegro.Extensions.Cqrs.Abstractions.Commands;
Expand All @@ -7,10 +6,7 @@

namespace Allegro.Extensions.Cqrs.Demo.Commands;

public record BarCommand(string Name) : ICommand
{
public string Id { get; } = Guid.NewGuid().ToString();
}
public record BarCommand(string Name) : Command;

internal class BarCommandFluentValidator : AbstractValidator<BarCommand>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
namespace Allegro.Extensions.Cqrs.Demo.Queries;

public record BarData(string SomeData);
internal record BarQuery(string? SomeId) : IQuery<BarData>;
internal record BarQuery(string? SomeId) : Query<BarData>;

internal class BarQueryFluentValidator : AbstractValidator<BarQuery>
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Allegro.Extensions.Cqrs.FluentValidations;

internal class FluentCommandValidator<T> : ICommandValidator<T>
where T : ICommand
where T : Command
{
private readonly IServiceProvider _serviceProvider;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
namespace Allegro.Extensions.Cqrs.FluentValidations;

internal class FluentQueryValidator<T> : IQueryValidator<T>
where T : IQuery
where T : Query
{
private readonly IServiceProvider _serviceProvider;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using FluentAssertions;
using Microsoft.Extensions.DependencyInjection;
using Xunit;

// ReSharper disable UnusedType.Local

namespace Allegro.Extensions.Cqrs.Tests.Unit;
Expand Down Expand Up @@ -37,10 +38,7 @@ public Task When_handler_is_missing_exception_will_be_thrown()
return act.Should().ThrowAsync<MissingCommandHandlerException>();
}

private record TestCommand : ICommand
{
public string Id { get; } = Guid.NewGuid().ToString();
}
private record TestCommand : Command;

private class TestCommandHandler : ICommandHandler<TestCommand>
{
Expand All @@ -58,10 +56,7 @@ public Task Handle(TestCommand command)
}
}

private record TestCommandNoHandler : ICommand
{
public string Id { get; } = Guid.NewGuid().ToString();
}
private record TestCommandNoHandler : Command;
}

public class CommandValidator
Expand All @@ -88,10 +83,7 @@ public async Task Command_validator_executed_before_command_action()
fixture.VerifyCommandActionsWereNotExecuted();
}

private record NotValidTestCommand : ICommand
{
public string Id { get; } = Guid.NewGuid().ToString();
}
private record NotValidTestCommand : Command;

private class TestCommandValidator : ICommandValidator<NotValidTestCommand>
{
Expand Down Expand Up @@ -143,10 +135,7 @@ public async Task Able_to_execute_command_actions()
fixture.VerifyCommandWithActionsWasHandled(command);
}

private record TestCommand : ICommand
{
public string Id { get; } = Guid.NewGuid().ToString();
}
private record TestCommand : Command;

private class TestCommandHandler : ICommandHandler<TestCommand>
{
Expand Down Expand Up @@ -214,14 +203,14 @@ public Fixture AddDecorator<TType, TDecorator>()
return this;
}

public void VerifyCommandWasHandled(ICommand testCommand)
public void VerifyCommandWasHandled(Command testCommand)
{
var storage = _provider!.GetRequiredService<CommandLog>();

storage.ExecutedCommandsLog.Single().Should().Be(testCommand.ToString());
}

public void VerifyCommandWithActionsWasHandled(ICommand testCommand)
public void VerifyCommandWithActionsWasHandled(Command testCommand)
{
var expectedLogs = new List<string>()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ public async Task Able_to_handle_query()
var fixture = new Fixture().Build();
var queryDispatcher = fixture.QueryDispatcher;

var result = await queryDispatcher.Query(new TestQuery(Guid.NewGuid().ToString()), CancellationToken.None);
var result = await queryDispatcher.Query(new TestQuery(), CancellationToken.None);
result.Should().Be(new TestData("TestData"));
}

[Fact]
public Task When_handler_is_missing_exception_will_be_thrown()
{
var queryDispatcher = new Fixture().Build().QueryDispatcher;
var act = () => queryDispatcher.Query(new TestQueryNoHandler(Guid.NewGuid().ToString()), CancellationToken.None);
var act = () => queryDispatcher.Query(new TestQueryNoHandler(), CancellationToken.None);

return act.Should().ThrowAsync<MissingQueryHandlerException>();
}

private record TestQuery(string Id) : IQuery<TestData>;
private record TestQuery : Query<TestData>;

private record TestData(string Name);

Expand All @@ -49,7 +49,7 @@ public Task<TestData> Handle(TestQuery query, CancellationToken cancellationToke
}
}

private record TestQueryNoHandler(string Id) : IQuery<TestData>;
private record TestQueryNoHandler : Query<TestData>;
}

public class QueryValidator
Expand All @@ -76,10 +76,7 @@ public async Task Query_validator_executed_before_query_decorator()
fixture.VerifyQueryDecoratorsWereNotExecuted();
}

private record NotValidTestQuery : IQuery<int>
{
public string Id { get; } = Guid.NewGuid().ToString();
}
private record NotValidTestQuery : Query<int>;

private class TestQueryValidator : IQueryValidator<NotValidTestQuery>
{
Expand Down Expand Up @@ -132,10 +129,7 @@ public async Task Able_to_execute_query_decorators()
fixture.VerifyQueryWithDecoratorWasHandled(query);
}

private record TestQuery : IQuery<int>
{
public string Id { get; } = Guid.NewGuid().ToString();
}
private record TestQuery : Query<int>;

private class TestQueryHandler : IQueryHandler<TestQuery, int>
{
Expand Down Expand Up @@ -203,7 +197,7 @@ public Fixture Build()

public IQueryDispatcher QueryDispatcher => _provider!.GetRequiredService<IQueryDispatcher>();

public void VerifyQueryWithDecoratorWasHandled(IQuery testQuery)
public void VerifyQueryWithDecoratorWasHandled(Query testQuery)
{
var expectedLogs = new List<string>()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ internal sealed class CommandDispatcher : ICommandDispatcher
public CommandDispatcher(IServiceProvider serviceProvider)
=> _serviceProvider = serviceProvider;

public async Task Send<TCommand>(TCommand command) where TCommand : ICommand
public async Task Send<TCommand>(TCommand command) where TCommand : Command
{
// TODO: maybe some configuration to reuse outer scope instead of creating new one
using var scope = _serviceProvider.CreateScope();
Expand All @@ -37,7 +37,7 @@ public async Task Send<TCommand>(TCommand command) where TCommand : ICommand

internal class MissingCommandHandlerException : Exception
{
public MissingCommandHandlerException(ICommand command) : base($"Missing handler for command {command.GetType().FullName}")
public MissingCommandHandlerException(Command command) : base($"Missing handler for command {command.GetType().FullName}")
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ internal sealed class QueryDispatcher : IQueryDispatcher
public QueryDispatcher(IServiceProvider serviceProvider)
=> _serviceProvider = serviceProvider;

public async Task<TResult> Query<TResult>(IQuery<TResult> query, CancellationToken cancellationToken)
public async Task<TResult> Query<TResult>(Query<TResult> query, CancellationToken cancellationToken)
{
// TODO: maybe some configuration to reuse outer scope instead of creating new one
using var scope = _serviceProvider.CreateScope();
Expand All @@ -24,7 +24,7 @@ public async Task<TResult> Query<TResult>(IQuery<TResult> query, CancellationTok
var queryValidators = scope.ServiceProvider.GetServices(validatorType);

var validateMethodInfo = validatorType
.GetMethod(nameof(IQueryValidator<IQuery<TResult>>.Validate));
.GetMethod(nameof(IQueryValidator<Query<TResult>>.Validate));

if (validateMethodInfo is null)
{
Expand Down Expand Up @@ -57,7 +57,7 @@ await Task.WhenAll(queryValidators.Select(p =>
}

var handleMethodInfo = handlerType
.GetMethod(nameof(IQueryHandler<IQuery<TResult>, TResult>.Handle));
.GetMethod(nameof(IQueryHandler<Query<TResult>, TResult>.Handle));

if (handleMethodInfo is null)
{
Expand All @@ -78,7 +78,7 @@ await Task.WhenAll(queryValidators.Select(p =>

internal class MissingQueryHandlerException<T> : MissingQueryHandlerException
{
public MissingQueryHandlerException(IQuery<T> query) : base($"Missing handler for query {query.GetType().FullName}")
public MissingQueryHandlerException(Query<T> query) : base($"Missing handler for query {query.GetType().FullName}")
{
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/Allegro.Extensions.Cqrs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres
to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2023-01-26

### Allegro.Extensions.Cqrs.Abstractions

Replace ICommand and IQuery with Command and Query (based on the approach
from [CQRS.Mediatr.Lite](https://github.com/microsoft/CQRS.Mediatr.Lite/blob/a6f63cf62a5e2b1b48a55b7917ba036c8ae6f3b9/src/sdk/Command/Command.cs))

## [1.1.0] - 2022-11-17

### Allegro.Extensions.Cqrs
Expand Down
4 changes: 2 additions & 2 deletions src/Allegro.Extensions.Cqrs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ In this package our custom implementation of tools and markers are delivered.

## Allegro.Extensions.Cqrs.Abstractions

Contains common CQRS set of markers and abstractions like `ICommand`, `IQuery<>`, `ICommandDispatcher`, `IQueryDispatcher`, `ICommandHandler`, `IQueryHandler`.
Contains common CQRS set of markers and abstractions like `Command`, `Query<>`, `ICommandDispatcher`, `IQueryDispatcher`, `ICommandHandler`, `IQueryHandler`.

Additionally we introduce some additional things like [Command and Query Validators](#icommandvalidator-and-iqueryvalidator), or [Fluent Validations](#fluent-validations).

Expand Down Expand Up @@ -133,5 +133,5 @@ For registrations [Scrutor](https://github.com/khellang/Scrutor) packages is use
- for learning purposes
- it is simple code, MediatR is still too much
- better separation of queries and commands for decorators (IPipelineBehavior doesn't allow for this)
- ICommand without return type
- Command without return type
- Good article why not: https://cezarypiatek.github.io/post/why-i-dont-use-mediatr-for-cqrs/
2 changes: 1 addition & 1 deletion src/Allegro.Extensions.Cqrs/version.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<Project>
<PropertyGroup>
<VersionPrefix>1.1.0</VersionPrefix>
<VersionPrefix>2.0.0</VersionPrefix>
</PropertyGroup>
</Project>

0 comments on commit c690161

Please sign in to comment.