Skip to content

Commit

Permalink
Merge pull request #251 from J-Tech-Japan/250-command-converter
Browse files Browse the repository at this point in the history
Command converter
  • Loading branch information
tomohisa authored Feb 29, 2024
2 parents 1a98300 + d8d3e97 commit 8c50db3
Show file tree
Hide file tree
Showing 25 changed files with 231 additions and 59 deletions.
2 changes: 2 additions & 0 deletions Sekiban.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,14 @@
</Patterns>
</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateInstanceFields/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=4a98fdf6_002D7d98_002D4f5a_002Dafeb_002Dea44ad98c70c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="FIELD" /&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CustomTools/CustomToolsData/@EntryValue"></s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=appsettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Dissolvable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ffffff/@EntryIndexedValue">True</s:Boolean>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using FeatureCheck.Domain.Aggregates.DerivedTypes.ValueObjects;
using Sekiban.Core.Command;
using System.ComponentModel.DataAnnotations;
namespace FeatureCheck.Domain.Aggregates.DerivedTypes.Commands;

public record CreateCar(
string Color,
[property: Required]
string Name) : ICommandConverter<DerivedTypeAggregate>
{
public Guid GetAggregateId() => Guid.NewGuid();
public class Handler : ICommandConverterHandler<DerivedTypeAggregate, CreateCar>
{
public ICommand<DerivedTypeAggregate> ConvertCommand(CreateCar command) => new CreateVehicle(new Car(command.Color, command.Name));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ public IEnumerable<IEventPayloadApplicableTo<DerivedTypeAggregate>> HandleComman
yield return new DerivedTypeCreated(command.Vehicle);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using System.ComponentModel.DataAnnotations;
namespace FeatureCheck.Domain.Aggregates.DerivedTypes.ValueObjects;

public record Car(string Color, string Name) : IVehicle
public record Car(
[property: Required]
string Color,
string Name) : IVehicle
{
public static Car Empty => new(string.Empty, string.Empty);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
using System.Text.Json.Serialization;
namespace FeatureCheck.Domain.Aggregates.DerivedTypes.ValueObjects;

[JsonDerivedType(typeof(Bike), nameof(Bike))]
[JsonDerivedType(typeof(Car), nameof(Car))]
public interface IVehicle;
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,8 @@ public override void Define()
.AddCommandHandler<BookingCommands.BookRoom, BookingCommands.BookRoom.Handler>()
.AddCommandHandler<BookingCommands.PayBookedRoom, BookingCommands.PayBookedRoom.Handler>();

AddAggregate<DerivedTypeAggregate>().AddCommandHandler<CreateVehicle, CreateVehicle.Handler>();
AddAggregate<DerivedTypeAggregate>()
.AddCommandHandler<CreateVehicle, CreateVehicle.Handler>()
.AddCommandHandler<CreateCar, CreateCar.Handler>();
}
}
2 changes: 1 addition & 1 deletion internalUsages/FeatureCheck.WebApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
});

// Sekiban Web Setting
builder.AddSekibanWebFromDomainDependency<FeatureCheckDependency>(web => web.AllowAllIfLoggedIn());
builder.AddSekibanWebFromDomainDependency<FeatureCheckDependency>();
builder.Services.AddSwaggerGen(options => options.ConfigureForSekibanWeb());

builder.Services.AddEndpointsApiExplorer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>Sekiban.Aspire.Infrastructure.Cosmos</PackageId>
<Version>0.17.7-preview</Version>
<Version>0.17.8-preview</Version>
<Authors>J-Tech Group</Authors>
<Company>J-Tech-Japan</Company>
<PackageDescription>Sekiban - Event Sourcing Framework Cosmos Aspire Connector</PackageDescription>
<PackageVersion>0.17.7-preview</PackageVersion>
<PackageVersion>0.17.8-preview</PackageVersion>
<Description>Fix Retrieval Option returns snapshot</Description>
<RepositoryUrl>https://github.com/J-Tech-Japan/Sekiban</RepositoryUrl>
<RootNamespace>Sekiban.Aspire.Infrastructure.Cosmos</RootNamespace>
Expand All @@ -23,8 +23,8 @@

<ItemGroup>
<PackageReference Include="Aspire.Azure.Storage.Blobs" Version="8.0.0-preview.3.24105.21"/>
<PackageReference Include="Sekiban.Infrastructure.Cosmos" Version="0.17.7"/>
<PackageReference Include="Sekiban.Web" Version="0.17.7"/>
<PackageReference Include="Sekiban.Infrastructure.Cosmos" Version="0.17.8"/>
<PackageReference Include="Sekiban.Web" Version="0.17.8"/>
<ProjectReference Include="..\Sekiban.Infrastructure.Cosmos\Sekiban.Infrastructure.Cosmos.csproj"/>
<ProjectReference Include="..\Sekiban.Web\Sekiban.Web.csproj"/>
<None Include="..\README.md" Pack="true" PackagePath="\"/>
Expand Down
109 changes: 96 additions & 13 deletions src/Sekiban.Core/Command/CommandExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,75 @@ public class CommandExecutor(
public async Task<CommandExecutorResponse> ExecCommandAsync<TCommand>(TCommand command, List<CallHistory>? callHistories = null)
where TCommand : ICommandCommon
{


if (command is ICommandConverterCommon converter)
{
var validationResult = command.ValidateProperties().ToList();
if (validationResult.Count != 0)
{
return new CommandExecutorResponse(
null,
null,
0,
validationResult,
null,
converter.GetType().GetAggregatePayloadTypeFromCommandType().Name,
0);
}
var commandHandlerCommonType = typeof(ICommandHandlerCommon<,>).MakeGenericType(
converter.GetType().GetAggregatePayloadTypeFromCommandType(),
converter.GetType());
if (serviceProvider.GetService(commandHandlerCommonType) is ICommandConverterHandlerCommon handler)
{
if (((dynamic)handler).ConvertCommand((dynamic)converter) is ICommandCommon convertedCommand)
{
return await ExecCommandAsync(convertedCommand, callHistories);
}
}
}

if (!command.GetType().IsCommandType()) { throw new SekibanCommandNotRegisteredException(command.GetType().Name); }
var method = GetType().GetMethod(nameof(ExecCommandAsyncTyped)) ?? throw new MissingMethodException("Method not found");
var genericMethod = method.MakeGenericMethod(command.GetType().GetAggregatePayloadTypeFromCommandType(), command.GetType());
var (response, _)
= ((CommandExecutorResponse, List<IEvent>))await (dynamic)(genericMethod.Invoke(this, [command, callHistories]) ??
throw new SekibanCommandHandlerNotMatchException("Command failed to execute " + command.GetType().Name));

var (response, _) = ((CommandExecutorResponse, List<IEvent>))await (dynamic)(genericMethod.Invoke(this, [command, callHistories]) ??
throw new SekibanCommandHandlerNotMatchException("Command failed to execute " + command.GetType().Name));
return response;
}

public async Task<CommandExecutorResponseWithEvents> ExecCommandWithEventsAsync<TCommand>(
TCommand command,
List<CallHistory>? callHistories = null) where TCommand : ICommandCommon
{
if (command is ICommandConverterCommon converter)
{
var validationResult = command.ValidateProperties().ToList();
if (validationResult.Count != 0)
{
return new CommandExecutorResponseWithEvents(
new CommandExecutorResponse(
null,
null,
0,
validationResult,
null,
converter.GetType().GetAggregatePayloadTypeFromCommandType().Name,
0),
Enumerable.Empty<IEvent>().ToImmutableList());
}
var commandHandlerCommonType = typeof(ICommandHandlerCommon<,>).MakeGenericType(
converter.GetType().GetAggregatePayloadTypeFromCommandType(),
converter.GetType());
if (serviceProvider.GetService(commandHandlerCommonType) is ICommandConverterHandlerCommon handler)
{
if (((dynamic)handler).ConvertCommand((dynamic)converter) is ICommandCommon convertedCommand)
{
return await ExecCommandWithEventsAsync(convertedCommand, callHistories);
}
}
}

if (!command.GetType().IsCommandType()) { throw new SekibanCommandNotRegisteredException(command.GetType().Name); }
var method = GetType().GetMethod(nameof(ExecCommandAsyncTyped)) ?? throw new MissingMethodException("Method not found");
var genericMethod = method.MakeGenericMethod(command.GetType().GetAggregatePayloadTypeFromCommandType(), command.GetType());
Expand All @@ -53,19 +109,45 @@ public async Task<CommandExecutorResponseWithEvents> ExecCommandWithEventsAsync<
public async Task<CommandExecutorResponse> ExecCommandWithoutValidationAsync<TCommand>(TCommand command, List<CallHistory>? callHistories = null)
where TCommand : ICommandCommon
{
if (command is ICommandConverterCommon converter)
{
var commandHandlerCommonType = typeof(ICommandHandlerCommon<,>).MakeGenericType(
converter.GetType().GetAggregatePayloadTypeFromCommandType(),
converter.GetType());
if (serviceProvider.GetService(commandHandlerCommonType) is ICommandConverterHandlerCommon handler)
{
if (((dynamic)handler).ConvertCommand((dynamic)converter) is ICommandCommon convertedCommand)
{
return await ExecCommandWithoutValidationAsync(convertedCommand, callHistories);
}
}
}

if (!command.GetType().IsCommandType()) { throw new SekibanCommandNotRegisteredException(command.GetType().Name); }
var method = GetType().GetMethod(nameof(ExecCommandWithoutValidationAsyncTyped)) ?? throw new MissingMethodException("Method not found");
var genericMethod = method.MakeGenericMethod(command.GetType().GetAggregatePayloadTypeFromCommandType(), command.GetType());
var (response, _)
= ((CommandExecutorResponse, List<IEvent>))await (dynamic)(genericMethod.Invoke(this, [command, callHistories]) ??
throw new SekibanCommandHandlerNotMatchException("Command failed to execute " + command.GetType().Name));
var (response, _) = ((CommandExecutorResponse, List<IEvent>))await (dynamic)(genericMethod.Invoke(this, [command, callHistories]) ??
throw new SekibanCommandHandlerNotMatchException("Command failed to execute " + command.GetType().Name));
return response;
}

public async Task<CommandExecutorResponseWithEvents> ExecCommandWithoutValidationWithEventsAsync<TCommand>(
TCommand command,
List<CallHistory>? callHistories = null) where TCommand : ICommandCommon
{
if (command is ICommandConverterCommon converter)
{
var commandHandlerCommonType = typeof(ICommandHandlerCommon<,>).MakeGenericType(
converter.GetType().GetAggregatePayloadTypeFromCommandType(),
converter.GetType());
if (serviceProvider.GetService(commandHandlerCommonType) is ICommandConverterHandlerCommon handler)
{
if (((dynamic)handler).ConvertCommand((dynamic)converter) is ICommandCommon convertedCommand)
{
return await ExecCommandWithoutValidationWithEventsAsync(convertedCommand, callHistories);
}
}
}
if (!command.GetType().IsCommandType()) { throw new SekibanCommandNotRegisteredException(command.GetType().Name); }
var method = GetType().GetMethod(nameof(ExecCommandWithoutValidationAsyncTyped)) ?? throw new MissingMethodException("Method not found");
var genericMethod = method.MakeGenericMethod(command.GetType().GetAggregatePayloadTypeFromCommandType(), command.GetType());
Expand All @@ -81,16 +163,19 @@ public async Task<CommandExecutorResponseWithEvents> ExecCommandWithoutValidatio
where TCommand : ICommand<TAggregatePayload>
{
var validationResult = command.ValidateProperties().ToList();
return validationResult.Count != 0
? (new CommandExecutorResponse(
if (validationResult.Count != 0)
{
return (new CommandExecutorResponse(
null,
null,
0,
validationResult,
null,
GetAggregatePayloadOut<TAggregatePayload>(Enumerable.Empty<IEvent>()),
0), Enumerable.Empty<IEvent>().ToList())
: await ExecCommandWithoutValidationAsyncTyped<TAggregatePayload, TCommand>(command, callHistories);
0), Enumerable.Empty<IEvent>().ToList());
}

return await ExecCommandWithoutValidationAsyncTyped<TAggregatePayload, TCommand>(command, callHistories);
}

public async Task<(CommandExecutorResponse, List<IEvent>)> ExecCommandWithoutValidationAsyncTyped<TAggregatePayload, TCommand>(
Expand Down Expand Up @@ -144,9 +229,7 @@ var handler
var adapter = Activator.CreateInstance(adapterClass) ?? throw new MissingMethodException("Method not found");
var method = adapterClass.GetMethod("HandleCommandAsync") ?? throw new MissingMethodException("HandleCommandAsync not found");
var commandResponse
= (CommandResponse)await ((dynamic?)method.Invoke(
adapter,
[commandDocument, handler, aggregateId, rootPartitionKey]) ??
= (CommandResponse)await ((dynamic?)method.Invoke(adapter, [commandDocument, handler, aggregateId, rootPartitionKey]) ??
throw new SekibanCommandHandlerNotMatchException("Command failed to execute " + command.GetType().Name));
events = await HandleEventsAsync<TAggregatePayload, TCommand>(commandResponse.Events, commandDocument);
version = commandResponse.Version;
Expand Down
2 changes: 1 addition & 1 deletion src/Sekiban.Core/Command/ICommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ public interface ICommand<TAggregatePayload> : ICommandCommon where TAggregatePa
/// </summary>
/// <returns></returns>
public Guid GetAggregateId();
}
}
7 changes: 7 additions & 0 deletions src/Sekiban.Core/Command/ICommandConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
using Sekiban.Core.Aggregate;
namespace Sekiban.Core.Command;

public interface ICommandConverter<TAggregatePayload> : ICommand<TAggregatePayload>, ICommandConverterCommon
where TAggregatePayload : IAggregatePayloadCommon
{
}
3 changes: 3 additions & 0 deletions src/Sekiban.Core/Command/ICommandConverterCommon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Sekiban.Core.Command;

public interface ICommandConverterCommon;
8 changes: 8 additions & 0 deletions src/Sekiban.Core/Command/ICommandConverterHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Sekiban.Core.Aggregate;
namespace Sekiban.Core.Command;

public interface ICommandConverterHandler<TAggregatePayload, TCommand> : ICommandHandlerCommon<TAggregatePayload, TCommand>,
ICommandConverterHandlerCommon where TAggregatePayload : IAggregatePayloadCommon where TCommand : ICommandConverter<TAggregatePayload>
{
public ICommand<TAggregatePayload> ConvertCommand(TCommand command);
}
3 changes: 3 additions & 0 deletions src/Sekiban.Core/Command/ICommandConverterHandlerCommon.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace Sekiban.Core.Command;

public interface ICommandConverterHandlerCommon;
4 changes: 2 additions & 2 deletions src/Sekiban.Core/Sekiban.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
<Nullable>enable</Nullable>
<LangVersion>preview</LangVersion>
<PackageId>Sekiban.Core</PackageId>
<Version>0.17.7</Version>
<Version>0.17.8</Version>
<Authors>J-Tech Group</Authors>
<Company>J-Tech-Japan</Company>
<PackageDescription>Sekiban - Event Sourcing Framework Core</PackageDescription>
<RepositoryUrl>https://github.com/J-Tech-Japan/Sekiban</RepositoryUrl>
<PackageVersion>0.17.7</PackageVersion>
<PackageVersion>0.17.8</PackageVersion>
<Description>Fix Retrieval Option returns snapshot</Description>
<AssemblyName>Sekiban.Core</AssemblyName>
<RootNamespace>Sekiban.Core</RootNamespace>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<PackageId>Sekiban.Infrastructure.Azure.Storage.Blobs</PackageId>
<Version>0.17.7</Version>
<Version>0.17.8</Version>
<Authors>J-Tech Group</Authors>
<Company>J-Tech-Japan</Company>
<PackageDescription>Sekiban - Event Sourcing Framework Azure Storage Blob</PackageDescription>
<RepositoryUrl>https://github.com/J-Tech-Japan/Sekiban</RepositoryUrl>
<PackageVersion>0.17.7</PackageVersion>
<PackageVersion>0.17.8</PackageVersion>
<Description>Fix Retrieval Option returns snapshot</Description>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<LangVersion>preview</LangVersion>
Expand All @@ -24,7 +24,7 @@
<ItemGroup>
<None Include="..\README.md" Pack="true" PackagePath="\"/>
<PackageReference Include="Azure.Storage.Blobs" Version="12.19.1"/>
<PackageReference Include="Sekiban.Core" Version="0.17.7"/>
<PackageReference Include="Sekiban.Core" Version="0.17.8"/>
<PackageReference Include="System.Configuration.ConfigurationManager" Version="8.0.0"/>
</ItemGroup>

Expand Down
Loading

0 comments on commit 8c50db3

Please sign in to comment.