From 8f87e34421e77bb8bb7081c9782f3f2dfbb297f7 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Wed, 20 Nov 2019 22:46:27 +0000 Subject: [PATCH 01/22] Update command sender --- Kledex.sln | 1 - src/Kledex/Commands/CommandSender.cs | 52 ++++++++++++++++--- src/Kledex/Commands/ICommandSender.cs | 16 ++++++ .../Commands/ISequenceCommandHandlerAsync.cs | 9 ++++ 4 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 src/Kledex/Commands/ISequenceCommandHandlerAsync.cs diff --git a/Kledex.sln b/Kledex.sln index 49a1a875..19e053f2 100644 --- a/Kledex.sln +++ b/Kledex.sln @@ -21,7 +21,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets LICENSE = LICENSE - package.json = package.json README.md = README.md EndProjectSection EndProject diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index 0990d43a..7d225ca0 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -42,17 +42,29 @@ public CommandSender(IHandlerResolver handlerResolver, /// public async Task SendAsync(ICommand command) { - await ProcessAsync(command); + await ProcessAsync(command, () => GetCommandResponseAsync(command)); } /// - public async Task SendAsync(ICommand command) + public Task SendAsync(params ICommand[] commands) { - var response = await ProcessAsync(command); + return ProcessAsync(commands); + } + /// + public async Task SendAsync(ICommand command) + { + var response = await ProcessAsync(command, () => GetCommandResponseAsync(command)); return response?.Result != null ? (TResult)response.Result : default; } + /// + public async Task SendAsync(params ICommand[] commands) + { + var lastStepReponse = await ProcessAsync(commands); + return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; + } + /// public void Send(ICommand command) { @@ -63,11 +75,10 @@ public void Send(ICommand command) public TResult Send(ICommand command) { var response = Process(command); - return response?.Result != null ? (TResult)response.Result : default; } - private async Task ProcessAsync(ICommand command) + private async Task ProcessAsync(ICommand command, Func> getResponse) { if (command == null) { @@ -79,9 +90,7 @@ private async Task ProcessAsync(ICommand command) await _validationService.ValidateAsync(command); } - var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandlerAsync<>)); - var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType() }); - var response = await (Task)handleMethod.Invoke(handler, new object[] { command }); + var response = await getResponse(); if (response == null) { @@ -113,6 +122,19 @@ await _storeProvider.SaveAsync(GetAggregateType(domainCommand), return response; } + private async Task ProcessAsync(params ICommand[] commands) + { + CommandResponse lastStepResponse = null; + + foreach (var command in commands) + { + var response = await ProcessAsync(command, () => GetSequenceCommandResponseAsync(command, lastStepResponse)); + lastStepResponse = response; + } + + return lastStepResponse; + } + private CommandResponse Process(ICommand command) { if (command == null) @@ -166,5 +188,19 @@ private static Type GetAggregateType(IDomainCommand domainCommand) var aggregateType = commandInterface.GetGenericArguments().FirstOrDefault(); return aggregateType; } + + private Task GetCommandResponseAsync(ICommand command) + { + var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandlerAsync<>)); + var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType() }); + return (Task)handleMethod.Invoke(handler, new object[] { command }); + } + + private Task GetSequenceCommandResponseAsync(ICommand command, CommandResponse previousStepResponse) + { + var handler = _handlerResolver.ResolveHandler(command, typeof(ISequenceCommandHandlerAsync<>)); + var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType(), typeof(CommandResponse) }); + return (Task)handleMethod.Invoke(handler, new object[] { command, previousStepResponse }); + } } } diff --git a/src/Kledex/Commands/ICommandSender.cs b/src/Kledex/Commands/ICommandSender.cs index 9305ef01..160cf87b 100644 --- a/src/Kledex/Commands/ICommandSender.cs +++ b/src/Kledex/Commands/ICommandSender.cs @@ -15,6 +15,13 @@ public interface ICommandSender /// The command. Task SendAsync(ICommand command); + /// + /// Sends the specified commands asynchronously. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. + /// + /// The command. + Task SendAsync(params ICommand[] commands); + /// /// Sends the specified command asynchronously. /// The command handler must implement Kledex.Commands.ICommandHandlerAsync<TCommand>. @@ -23,6 +30,15 @@ public interface ICommandSender /// A custom object set as result in the command hadler response. Task SendAsync(ICommand command); + /// + /// Sends the specified commands asynchronously. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. + /// + /// The type of the result. + /// The commands. + /// A custom object set as result in the command hadler response. + Task SendAsync(params ICommand[] commands); + /// /// Sends the specified command. /// The command handler must implement Kledex.Commands.ICommandHandler<TCommand>. diff --git a/src/Kledex/Commands/ISequenceCommandHandlerAsync.cs b/src/Kledex/Commands/ISequenceCommandHandlerAsync.cs new file mode 100644 index 00000000..9920dda8 --- /dev/null +++ b/src/Kledex/Commands/ISequenceCommandHandlerAsync.cs @@ -0,0 +1,9 @@ +using System.Threading.Tasks; + +namespace Kledex.Commands +{ + public interface ISequenceCommandHandlerAsync where TCommand : ICommand + { + Task HandleAsync(TCommand command, CommandResponse previousStepResponse); + } +} From 7bb978fc17dda15f083b8113491a85de78002c73 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 09:00:41 +0000 Subject: [PATCH 02/22] Update command sender --- src/Kledex/Commands/CommandSender.cs | 78 ++++++++++++++-------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index 7d225ca0..863ccb55 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -39,6 +39,14 @@ public CommandSender(IHandlerResolver handlerResolver, _options = options.Value; } + private static Type GetAggregateType(IDomainCommand domainCommand) + { + var commandType = domainCommand.GetType(); + var commandInterface = commandType.GetInterfaces()[1]; + var aggregateType = commandInterface.GetGenericArguments().FirstOrDefault(); + return aggregateType; + } + /// public async Task SendAsync(ICommand command) { @@ -65,19 +73,6 @@ public async Task SendAsync(params ICommand[] commands) return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - /// - public void Send(ICommand command) - { - Process(command); - } - - /// - public TResult Send(ICommand command) - { - var response = Process(command); - return response?.Result != null ? (TResult)response.Result : default; - } - private async Task ProcessAsync(ICommand command, Func> getResponse) { if (command == null) @@ -88,7 +83,7 @@ private async Task ProcessAsync(ICommand command, Func ProcessAsync(ICommand command, Func)response.Events); } @@ -135,6 +130,33 @@ private async Task ProcessAsync(params ICommand[] commands) return lastStepResponse; } + private Task GetCommandResponseAsync(ICommand command) + { + var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandlerAsync<>)); + var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType() }); + return (Task)handleMethod.Invoke(handler, new object[] { command }); + } + + private Task GetSequenceCommandResponseAsync(ICommand command, CommandResponse previousStepResponse) + { + var handler = _handlerResolver.ResolveHandler(command, typeof(ISequenceCommandHandlerAsync<>)); + var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType(), typeof(CommandResponse) }); + return (Task)handleMethod.Invoke(handler, new object[] { command, previousStepResponse }); + } + + /// + public void Send(ICommand command) + { + Process(command); + } + + /// + public TResult Send(ICommand command) + { + var response = Process(command); + return response?.Result != null ? (TResult)response.Result : default; + } + private CommandResponse Process(ICommand command) { if (command == null) @@ -180,27 +202,5 @@ private CommandResponse Process(ICommand command) return response; } - - private static Type GetAggregateType(IDomainCommand domainCommand) - { - var commandType = domainCommand.GetType(); - var commandInterface = commandType.GetInterfaces()[1]; - var aggregateType = commandInterface.GetGenericArguments().FirstOrDefault(); - return aggregateType; - } - - private Task GetCommandResponseAsync(ICommand command) - { - var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandlerAsync<>)); - var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType() }); - return (Task)handleMethod.Invoke(handler, new object[] { command }); - } - - private Task GetSequenceCommandResponseAsync(ICommand command, CommandResponse previousStepResponse) - { - var handler = _handlerResolver.ResolveHandler(command, typeof(ISequenceCommandHandlerAsync<>)); - var handleMethod = handler.GetType().GetMethod("HandleAsync", new[] { command.GetType(), typeof(CommandResponse) }); - return (Task)handleMethod.Invoke(handler, new object[] { command, previousStepResponse }); - } } } From 985a6e12764c32dc875d134f59435674e0826d26 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 09:05:30 +0000 Subject: [PATCH 03/22] Update dispatcher --- src/Kledex/Dispatcher.cs | 12 ++++++++++++ src/Kledex/IDispatcher.cs | 16 ++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/Kledex/Dispatcher.cs b/src/Kledex/Dispatcher.cs index 54274a0a..30313b8c 100644 --- a/src/Kledex/Dispatcher.cs +++ b/src/Kledex/Dispatcher.cs @@ -36,12 +36,24 @@ public Task SendAsync(ICommand command) return _commandSender.SendAsync(command); } + /// + public Task SendAsync(params ICommand[] commands) + { + return _commandSender.SendAsync(commands); + } + /// public Task SendAsync(ICommand command) { return _commandSender.SendAsync(command); } + /// + public Task SendAsync(params ICommand[] commands) + { + return _commandSender.SendAsync(commands); + } + /// public Task PublishAsync(TEvent @event) where TEvent : IEvent diff --git a/src/Kledex/IDispatcher.cs b/src/Kledex/IDispatcher.cs index 9004dd43..45474ad1 100644 --- a/src/Kledex/IDispatcher.cs +++ b/src/Kledex/IDispatcher.cs @@ -18,6 +18,13 @@ public interface IDispatcher /// The command. Task SendAsync(ICommand command); + /// + /// Sends the specified commands asynchronously. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. + /// + /// The command. + Task SendAsync(params ICommand[] commands); + /// /// Sends the specified command asynchronously. /// The command handler must implement Kledex.Commands.ICommandHandlerAsync<TCommand>. @@ -26,6 +33,15 @@ public interface IDispatcher /// A custom object set as result in the command hadler response. Task SendAsync(ICommand command); + /// + /// Sends the specified commands asynchronously. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. + /// + /// The type of the result. + /// The commands. + /// A custom object set as result in the command hadler response. + Task SendAsync(params ICommand[] commands); + /// /// Asynchronously publishes the specified event. /// The event handler must implement Kledex.Events.IEventHandlerAsync<TEvent>. From bb2dd4a265d113fbb13f2cab494b9bf9382f8752 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 09:32:43 +0000 Subject: [PATCH 04/22] Add command sequence sample project --- Kledex.sln | 7 ++++ .../Commands/FirstCommand.cs | 14 +++++++ .../Commands/SecondCommand.cs | 8 ++++ .../Commands/ThirdCommand.cs | 8 ++++ .../Handlers/FirstCommandHandler.cs | 20 ++++++++++ .../Handlers/SecondCommandHandler.cs | 20 ++++++++++ .../Handlers/ThirdCommandHandler.cs | 20 ++++++++++ .../Kledex.Sample.CommandSequence.csproj | 17 +++++++++ .../Kledex.Sample.CommandSequence/Program.cs | 37 +++++++++++++++++++ .../Validators/FirstCommandValidator.cs | 15 ++++++++ 10 files changed, 166 insertions(+) create mode 100644 samples/Kledex.Sample.CommandSequence/Commands/FirstCommand.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Commands/SecondCommand.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Commands/ThirdCommand.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Handlers/FirstCommandHandler.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Handlers/SecondCommandHandler.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Handlers/ThirdCommandHandler.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Kledex.Sample.CommandSequence.csproj create mode 100644 samples/Kledex.Sample.CommandSequence/Program.cs create mode 100644 samples/Kledex.Sample.CommandSequence/Validators/FirstCommandValidator.cs diff --git a/Kledex.sln b/Kledex.sln index 19e053f2..c20fe484 100644 --- a/Kledex.sln +++ b/Kledex.sln @@ -94,6 +94,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{05D6EE8E-C35 docs\assets\img\SendCommandFlow.svg = docs\assets\img\SendCommandFlow.svg EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kledex.Sample.CommandSequence", "samples\Kledex.Sample.CommandSequence\Kledex.Sample.CommandSequence.csproj", "{6DB19E10-1942-44CC-B592-01A34CEE8C77}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -176,6 +178,10 @@ Global {60755BA7-4F80-4AEC-8B0E-32414C1C6553}.Debug|Any CPU.Build.0 = Debug|Any CPU {60755BA7-4F80-4AEC-8B0E-32414C1C6553}.Release|Any CPU.ActiveCfg = Release|Any CPU {60755BA7-4F80-4AEC-8B0E-32414C1C6553}.Release|Any CPU.Build.0 = Release|Any CPU + {6DB19E10-1942-44CC-B592-01A34CEE8C77}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6DB19E10-1942-44CC-B592-01A34CEE8C77}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6DB19E10-1942-44CC-B592-01A34CEE8C77}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6DB19E10-1942-44CC-B592-01A34CEE8C77}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -202,6 +208,7 @@ Global {60755BA7-4F80-4AEC-8B0E-32414C1C6553} = {A9692D21-8091-4A3F-8F7C-54B821EFDF97} {7883593A-6225-4485-8EAE-E4EF3B799285} = {1282A8FB-D7A5-4562-8B9B-1C27B83EE580} {05D6EE8E-C356-4BF1-A015-90471F4E682E} = {7883593A-6225-4485-8EAE-E4EF3B799285} + {6DB19E10-1942-44CC-B592-01A34CEE8C77} = {FF129AB8-1B8D-4BAA-AB1F-39D2FA497397} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {154500C3-9A83-4F15-9D78-E1C285AD80AE} diff --git a/samples/Kledex.Sample.CommandSequence/Commands/FirstCommand.cs b/samples/Kledex.Sample.CommandSequence/Commands/FirstCommand.cs new file mode 100644 index 00000000..45d44433 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Commands/FirstCommand.cs @@ -0,0 +1,14 @@ +using Kledex.Commands; + +namespace Kledex.Sample.CommandSequence.Commands +{ + public class FirstCommand : Command + { + public string Name { get; set; } + + public FirstCommand() + { + Validate = true; + } + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Commands/SecondCommand.cs b/samples/Kledex.Sample.CommandSequence/Commands/SecondCommand.cs new file mode 100644 index 00000000..d36abae5 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Commands/SecondCommand.cs @@ -0,0 +1,8 @@ +using Kledex.Commands; + +namespace Kledex.Sample.CommandSequence.Commands +{ + public class SecondCommand : Command + { + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Commands/ThirdCommand.cs b/samples/Kledex.Sample.CommandSequence/Commands/ThirdCommand.cs new file mode 100644 index 00000000..72f143c5 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Commands/ThirdCommand.cs @@ -0,0 +1,8 @@ +using Kledex.Commands; + +namespace Kledex.Sample.CommandSequence.Commands +{ + public class ThirdCommand : Command + { + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Handlers/FirstCommandHandler.cs b/samples/Kledex.Sample.CommandSequence/Handlers/FirstCommandHandler.cs new file mode 100644 index 00000000..cac441a8 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Handlers/FirstCommandHandler.cs @@ -0,0 +1,20 @@ +using Kledex.Commands; +using Kledex.Sample.CommandSequence.Commands; +using System; +using System.Threading.Tasks; + +namespace Kledex.Sample.CommandSequence.Handlers +{ + public class FirstCommandHandler : ISequenceCommandHandlerAsync + { + public Task HandleAsync(FirstCommand command, CommandResponse previousStepResponse) + { + Console.WriteLine("Message from first command handler"); + + return Task.FromResult(new CommandResponse + { + Result = "First result" + }); + } + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Handlers/SecondCommandHandler.cs b/samples/Kledex.Sample.CommandSequence/Handlers/SecondCommandHandler.cs new file mode 100644 index 00000000..92cb9a19 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Handlers/SecondCommandHandler.cs @@ -0,0 +1,20 @@ +using Kledex.Commands; +using Kledex.Sample.CommandSequence.Commands; +using System; +using System.Threading.Tasks; + +namespace Kledex.Sample.CommandSequence.Handlers +{ + public class SecondCommandHandler : ISequenceCommandHandlerAsync + { + public Task HandleAsync(SecondCommand command, CommandResponse previousStepResponse) + { + Console.WriteLine($"Message from second command handler. Result from first handler: {previousStepResponse.Result}"); + + return Task.FromResult(new CommandResponse + { + Result = "Second result" + }); + } + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Handlers/ThirdCommandHandler.cs b/samples/Kledex.Sample.CommandSequence/Handlers/ThirdCommandHandler.cs new file mode 100644 index 00000000..20fe8145 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Handlers/ThirdCommandHandler.cs @@ -0,0 +1,20 @@ +using Kledex.Commands; +using Kledex.Sample.CommandSequence.Commands; +using System; +using System.Threading.Tasks; + +namespace Kledex.Sample.CommandSequence.Handlers +{ + public class ThirdCommandHandler : ISequenceCommandHandlerAsync + { + public Task HandleAsync(ThirdCommand command, CommandResponse previousStepResponse) + { + Console.WriteLine($"Message from third command handler. Result from second handler: {previousStepResponse.Result}"); + + return Task.FromResult(new CommandResponse + { + Result = "Third result" + }); + } + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Kledex.Sample.CommandSequence.csproj b/samples/Kledex.Sample.CommandSequence/Kledex.Sample.CommandSequence.csproj new file mode 100644 index 00000000..f6df4e30 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Kledex.Sample.CommandSequence.csproj @@ -0,0 +1,17 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + + diff --git a/samples/Kledex.Sample.CommandSequence/Program.cs b/samples/Kledex.Sample.CommandSequence/Program.cs new file mode 100644 index 00000000..3a4690c3 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Program.cs @@ -0,0 +1,37 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using Kledex.Extensions; +using Kledex.Sample.CommandSequence.Commands; +using Kledex.Validation.FluentValidation; + +namespace Kledex.Sample.CommandSequence +{ + class Program + { + static void Main(string[] args) + { + IServiceProvider serviceProvider = ConfigureServices(); + + var dispatcher = serviceProvider.GetService(); + + dispatcher.SendAsync( + new FirstCommand { Name = "My Name" }, + new SecondCommand(), + new ThirdCommand()) + .GetAwaiter().GetResult(); + + Console.ReadLine(); + } + + private static IServiceProvider ConfigureServices() + { + IServiceCollection services = new ServiceCollection(); + + services + .AddKledex(typeof(Program)) + .AddFluentValidationProvider(); + + return services.BuildServiceProvider(); + } + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Validators/FirstCommandValidator.cs b/samples/Kledex.Sample.CommandSequence/Validators/FirstCommandValidator.cs new file mode 100644 index 00000000..fbe0d052 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Validators/FirstCommandValidator.cs @@ -0,0 +1,15 @@ +using FluentValidation; +using Kledex.Sample.CommandSequence.Commands; + +namespace Kledex.Sample.CommandSequence.Validators +{ + public class FirstCommandValidator : AbstractValidator + { + public FirstCommandValidator() + { + RuleFor(c => c.Name) + .NotEmpty().WithMessage("Name is required.") + .Length(1, 100).WithMessage("Name length must be between 1 and 100 characters."); + } + } +} From 0e3b9e5329d63cd2541e8b798d1f4b8449b390b5 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 09:48:37 +0000 Subject: [PATCH 05/22] Update command sequence sample project --- .../Kledex.Sample.CommandSequence/Program.cs | 4 ++- src/Kledex/Commands/CommandSender.cs | 26 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/samples/Kledex.Sample.CommandSequence/Program.cs b/samples/Kledex.Sample.CommandSequence/Program.cs index 3a4690c3..c577db2c 100644 --- a/samples/Kledex.Sample.CommandSequence/Program.cs +++ b/samples/Kledex.Sample.CommandSequence/Program.cs @@ -14,12 +14,14 @@ static void Main(string[] args) var dispatcher = serviceProvider.GetService(); - dispatcher.SendAsync( + var result = dispatcher.SendAsync( new FirstCommand { Name = "My Name" }, new SecondCommand(), new ThirdCommand()) .GetAwaiter().GetResult(); + Console.WriteLine($"Final result: {result}"); + Console.ReadLine(); } diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index 863ccb55..e9b87b06 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -73,6 +73,19 @@ public async Task SendAsync(params ICommand[] commands) return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } + private async Task ProcessAsync(params ICommand[] commands) + { + CommandResponse lastStepResponse = null; + + foreach (var command in commands) + { + var response = await ProcessAsync(command, () => GetSequenceCommandResponseAsync(command, lastStepResponse)); + lastStepResponse = response; + } + + return lastStepResponse; + } + private async Task ProcessAsync(ICommand command, Func> getResponse) { if (command == null) @@ -117,19 +130,6 @@ await _storeProvider.SaveAsync(GetAggregateType(domainCommand), return response; } - private async Task ProcessAsync(params ICommand[] commands) - { - CommandResponse lastStepResponse = null; - - foreach (var command in commands) - { - var response = await ProcessAsync(command, () => GetSequenceCommandResponseAsync(command, lastStepResponse)); - lastStepResponse = response; - } - - return lastStepResponse; - } - private Task GetCommandResponseAsync(ICommand command) { var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandlerAsync<>)); From b03b6925187f4de9e8dc1ea49cfa877a6b435035 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 10:13:03 +0000 Subject: [PATCH 06/22] Update version --- README.md | 28 ++++++++++++++-------------- src/Directory.Build.props | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 37c6cba1..ce2466e4 100644 --- a/README.md +++ b/README.md @@ -18,17 +18,17 @@ With Kledex you can automatically dispatch events to a message bus (Service Bus | Package | Latest Stable | | --- | --- | -| [Kledex](https://www.nuget.org/packages/Kledex) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex) | -| [Kledex.Store.Cosmos.Mongo](https://www.nuget.org/packages/Kledex.Store.Cosmos.Mongo) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.Cosmos.Mongo) | -| [Kledex.Store.Cosmos.Sql](https://www.nuget.org/packages/Kledex.Store.Cosmos.Sql) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.Cosmos.Sql) | -| [Kledex.Store.EF.MySql](https://www.nuget.org/packages/Kledex.Store.EF.MySql) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.MySql) | -| [Kledex.Store.EF.PostgreSql](https://www.nuget.org/packages/Kledex.Store.EF.PostgreSql) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.PostgreSql) | -| [Kledex.Store.EF.Sqlite](https://www.nuget.org/packages/Kledex.Store.EF.Sqlite) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.Sqlite) | -| [Kledex.Store.EF.SqlServer](https://www.nuget.org/packages/Kledex.Store.EF.SqlServer) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.SqlServer) | -| [Kledex.Store.EF.InMemory](https://www.nuget.org/packages/Kledex.Store.EF.InMemory) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.InMemory) | -| [Kledex.Bus.ServiceBus](https://www.nuget.org/packages/Kledex.Bus.ServiceBus) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Bus.ServiceBus) | -| [Kledex.Bus.RabbitMQ](https://www.nuget.org/packages/Kledex.Bus.RabbitMQ) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Bus.RabbitMQ) | -| [Kledex.Validation.FluentValidation](https://www.nuget.org/packages/Kledex.Validation.FluentValidation) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Validation.FluentValidation) | -| [Kledex.Caching.Memory](https://www.nuget.org/packages/Kledex.Caching.Memory) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Caching.Memory) | -| [Kledex.Caching.Redis](https://www.nuget.org/packages/Kledex.Caching.Redis) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.Caching.Redis) | -| [Kledex.UI](https://www.nuget.org/packages/Kledex.UI) | [![Nuget Package](https://img.shields.io/badge/nuget-2.2.1-blue.svg)](https://www.nuget.org/packages/Kledex.UI) | +| [Kledex](https://www.nuget.org/packages/Kledex) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex) | +| [Kledex.Store.Cosmos.Mongo](https://www.nuget.org/packages/Kledex.Store.Cosmos.Mongo) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.Cosmos.Mongo) | +| [Kledex.Store.Cosmos.Sql](https://www.nuget.org/packages/Kledex.Store.Cosmos.Sql) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.Cosmos.Sql) | +| [Kledex.Store.EF.MySql](https://www.nuget.org/packages/Kledex.Store.EF.MySql) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.MySql) | +| [Kledex.Store.EF.PostgreSql](https://www.nuget.org/packages/Kledex.Store.EF.PostgreSql) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.PostgreSql) | +| [Kledex.Store.EF.Sqlite](https://www.nuget.org/packages/Kledex.Store.EF.Sqlite) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.Sqlite) | +| [Kledex.Store.EF.SqlServer](https://www.nuget.org/packages/Kledex.Store.EF.SqlServer) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.SqlServer) | +| [Kledex.Store.EF.InMemory](https://www.nuget.org/packages/Kledex.Store.EF.InMemory) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Store.EF.InMemory) | +| [Kledex.Bus.ServiceBus](https://www.nuget.org/packages/Kledex.Bus.ServiceBus) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Bus.ServiceBus) | +| [Kledex.Bus.RabbitMQ](https://www.nuget.org/packages/Kledex.Bus.RabbitMQ) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Bus.RabbitMQ) | +| [Kledex.Validation.FluentValidation](https://www.nuget.org/packages/Kledex.Validation.FluentValidation) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Validation.FluentValidation) | +| [Kledex.Caching.Memory](https://www.nuget.org/packages/Kledex.Caching.Memory) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Caching.Memory) | +| [Kledex.Caching.Redis](https://www.nuget.org/packages/Kledex.Caching.Redis) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.Caching.Redis) | +| [Kledex.UI](https://www.nuget.org/packages/Kledex.UI) | [![Nuget Package](https://img.shields.io/badge/nuget-2.3.0-blue.svg)](https://www.nuget.org/packages/Kledex.UI) | diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 55bdfd91..23aa69c5 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,7 +2,7 @@ - 2.2.1 + 2.3.0 true true Kledex, OpenCQRS, .net, core, mediator, ddd, cqrs, event sourcing, clean design, clean code, commands, queries, events From b3661ac48fd51312e85d84df2054cd17a435607f Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 11:17:04 +0000 Subject: [PATCH 07/22] Add ISequenceCommand --- .../Commands/SampleSequenceCommand.cs | 14 +++++++++++++ .../Kledex.Sample.CommandSequence/Program.cs | 6 ++---- src/Kledex/Commands/CommandSender.cs | 12 +++++------ src/Kledex/Commands/ICommandSender.cs | 8 ++++---- src/Kledex/Commands/ISequenceCommand.cs | 9 +++++++++ src/Kledex/Commands/SequenceCommand.cs | 20 +++++++++++++++++++ src/Kledex/Dispatcher.cs | 8 ++++---- src/Kledex/IDispatcher.cs | 8 ++++---- 8 files changed, 63 insertions(+), 22 deletions(-) create mode 100644 samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs create mode 100644 src/Kledex/Commands/ISequenceCommand.cs create mode 100644 src/Kledex/Commands/SequenceCommand.cs diff --git a/samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs b/samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs new file mode 100644 index 00000000..e1bc2e98 --- /dev/null +++ b/samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs @@ -0,0 +1,14 @@ +using Kledex.Commands; + +namespace Kledex.Sample.CommandSequence.Commands +{ + public class SampleSequenceCommand : SequenceCommand + { + public SampleSequenceCommand() + { + AddCommand(new FirstCommand { Name = "My Name" }); + AddCommand(new SecondCommand()); + AddCommand(new ThirdCommand()); + } + } +} diff --git a/samples/Kledex.Sample.CommandSequence/Program.cs b/samples/Kledex.Sample.CommandSequence/Program.cs index c577db2c..cb73b1f0 100644 --- a/samples/Kledex.Sample.CommandSequence/Program.cs +++ b/samples/Kledex.Sample.CommandSequence/Program.cs @@ -14,10 +14,8 @@ static void Main(string[] args) var dispatcher = serviceProvider.GetService(); - var result = dispatcher.SendAsync( - new FirstCommand { Name = "My Name" }, - new SecondCommand(), - new ThirdCommand()) + var result = dispatcher + .SendAsync(new SampleSequenceCommand()) .GetAwaiter().GetResult(); Console.WriteLine($"Final result: {result}"); diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index e9b87b06..8a0aec2f 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -54,9 +54,9 @@ public async Task SendAsync(ICommand command) } /// - public Task SendAsync(params ICommand[] commands) + public Task SendAsync(ISequenceCommand sequenceCommand) { - return ProcessAsync(commands); + return ProcessAsync(sequenceCommand); } /// @@ -67,17 +67,17 @@ public async Task SendAsync(ICommand command) } /// - public async Task SendAsync(params ICommand[] commands) + public async Task SendAsync(ISequenceCommand sequenceCommand) { - var lastStepReponse = await ProcessAsync(commands); + var lastStepReponse = await ProcessAsync(sequenceCommand); return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - private async Task ProcessAsync(params ICommand[] commands) + private async Task ProcessAsync(ISequenceCommand sequenceCommand) { CommandResponse lastStepResponse = null; - foreach (var command in commands) + foreach (var command in sequenceCommand.Commands) { var response = await ProcessAsync(command, () => GetSequenceCommandResponseAsync(command, lastStepResponse)); lastStepResponse = response; diff --git a/src/Kledex/Commands/ICommandSender.cs b/src/Kledex/Commands/ICommandSender.cs index 160cf87b..ff60442d 100644 --- a/src/Kledex/Commands/ICommandSender.cs +++ b/src/Kledex/Commands/ICommandSender.cs @@ -19,8 +19,8 @@ public interface ICommandSender /// Sends the specified commands asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// - /// The command. - Task SendAsync(params ICommand[] commands); + /// The sequence command. + Task SendAsync(ISequenceCommand sequenceCommand); /// /// Sends the specified command asynchronously. @@ -35,9 +35,9 @@ public interface ICommandSender /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The type of the result. - /// The commands. + /// The sequence command. /// A custom object set as result in the command hadler response. - Task SendAsync(params ICommand[] commands); + Task SendAsync(ISequenceCommand sequenceCommand); /// /// Sends the specified command. diff --git a/src/Kledex/Commands/ISequenceCommand.cs b/src/Kledex/Commands/ISequenceCommand.cs new file mode 100644 index 00000000..758c2a68 --- /dev/null +++ b/src/Kledex/Commands/ISequenceCommand.cs @@ -0,0 +1,9 @@ +using System.Collections.ObjectModel; + +namespace Kledex.Commands +{ + public interface ISequenceCommand + { + ReadOnlyCollection Commands { get; } + } +} diff --git a/src/Kledex/Commands/SequenceCommand.cs b/src/Kledex/Commands/SequenceCommand.cs new file mode 100644 index 00000000..85c3071e --- /dev/null +++ b/src/Kledex/Commands/SequenceCommand.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Kledex.Commands +{ + public abstract class SequenceCommand : ISequenceCommand + { + private readonly List _commands = new List(); + public ReadOnlyCollection Commands => _commands.AsReadOnly(); + + /// + /// Adds the command to the new command sequence collection. + /// + /// The command. + protected void AddCommand(ICommand command) + { + _commands.Add(command); + } + } +} diff --git a/src/Kledex/Dispatcher.cs b/src/Kledex/Dispatcher.cs index 30313b8c..47e4b4f0 100644 --- a/src/Kledex/Dispatcher.cs +++ b/src/Kledex/Dispatcher.cs @@ -37,9 +37,9 @@ public Task SendAsync(ICommand command) } /// - public Task SendAsync(params ICommand[] commands) + public Task SendAsync(ISequenceCommand sequenceCommand) { - return _commandSender.SendAsync(commands); + return _commandSender.SendAsync(sequenceCommand); } /// @@ -49,9 +49,9 @@ public Task SendAsync(ICommand command) } /// - public Task SendAsync(params ICommand[] commands) + public Task SendAsync(ISequenceCommand sequenceCommand) { - return _commandSender.SendAsync(commands); + return _commandSender.SendAsync(sequenceCommand); } /// diff --git a/src/Kledex/IDispatcher.cs b/src/Kledex/IDispatcher.cs index 45474ad1..2cc6b23d 100644 --- a/src/Kledex/IDispatcher.cs +++ b/src/Kledex/IDispatcher.cs @@ -22,8 +22,8 @@ public interface IDispatcher /// Sends the specified commands asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// - /// The command. - Task SendAsync(params ICommand[] commands); + /// The sequence command. + Task SendAsync(ISequenceCommand sequenceCommand); /// /// Sends the specified command asynchronously. @@ -38,9 +38,9 @@ public interface IDispatcher /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The type of the result. - /// The commands. + /// The sequence command. /// A custom object set as result in the command hadler response. - Task SendAsync(params ICommand[] commands); + Task SendAsync(ISequenceCommand sequenceCommand); /// /// Asynchronously publishes the specified event. From 0601fcbb44c86e6db7d8f7ce9e6a418db469b135 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 11:26:30 +0000 Subject: [PATCH 08/22] Update command sequence sample project --- samples/Kledex.Sample.CommandSequence/Program.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/Kledex.Sample.CommandSequence/Program.cs b/samples/Kledex.Sample.CommandSequence/Program.cs index cb73b1f0..1852e508 100644 --- a/samples/Kledex.Sample.CommandSequence/Program.cs +++ b/samples/Kledex.Sample.CommandSequence/Program.cs @@ -10,7 +10,7 @@ class Program { static void Main(string[] args) { - IServiceProvider serviceProvider = ConfigureServices(); + var serviceProvider = ConfigureServices(); var dispatcher = serviceProvider.GetService(); @@ -25,7 +25,7 @@ static void Main(string[] args) private static IServiceProvider ConfigureServices() { - IServiceCollection services = new ServiceCollection(); + var services = new ServiceCollection(); services .AddKledex(typeof(Program)) From c367ed1b6c5161347e1f736613cd70cb56a0850e Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 15:16:46 +0000 Subject: [PATCH 09/22] Add sync methods --- src/Kledex/Commands/CommandSender.cs | 56 ++++++++++++++++--- src/Kledex/Commands/ICommandSender.cs | 20 ++++++- .../Commands/ISequenceCommandHandler.cs | 7 +++ src/Kledex/Dispatcher.cs | 12 ++++ src/Kledex/IDispatcher.cs | 20 ++++++- 5 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/Kledex/Commands/ISequenceCommandHandler.cs diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index 8a0aec2f..658ff536 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -56,7 +56,7 @@ public async Task SendAsync(ICommand command) /// public Task SendAsync(ISequenceCommand sequenceCommand) { - return ProcessAsync(sequenceCommand); + return ProcessSequenceCommandAsync(sequenceCommand); } /// @@ -69,11 +69,11 @@ public async Task SendAsync(ICommand command) /// public async Task SendAsync(ISequenceCommand sequenceCommand) { - var lastStepReponse = await ProcessAsync(sequenceCommand); + var lastStepReponse = await ProcessSequenceCommandAsync(sequenceCommand); return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - private async Task ProcessAsync(ISequenceCommand sequenceCommand) + private async Task ProcessSequenceCommandAsync(ISequenceCommand sequenceCommand) { CommandResponse lastStepResponse = null; @@ -147,17 +147,43 @@ private Task GetSequenceCommandResponseAsync(ICommand command, /// public void Send(ICommand command) { - Process(command); + Process(command, () => GetCommandResponse(command)); + } + + /// + public void Send(ISequenceCommand sequenceCommand) + { + ProcessSequenceCommand(sequenceCommand); } /// public TResult Send(ICommand command) { - var response = Process(command); + var response = Process(command, () => GetCommandResponse(command)); return response?.Result != null ? (TResult)response.Result : default; } - private CommandResponse Process(ICommand command) + /// + public TResult Send(ISequenceCommand sequenceCommand) + { + var lastStepReponse = ProcessSequenceCommand(sequenceCommand); + return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; + } + + private CommandResponse ProcessSequenceCommand(ISequenceCommand sequenceCommand) + { + CommandResponse lastStepResponse = null; + + foreach (var command in sequenceCommand.Commands) + { + var response = Process(command, () => GetSequenceCommandResponse(command, lastStepResponse)); + lastStepResponse = response; + } + + return lastStepResponse; + } + + private CommandResponse Process(ICommand command, Func getResponse) { if (command == null) { @@ -169,9 +195,7 @@ private CommandResponse Process(ICommand command) _validationService.Validate(command); } - var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandler<>)); - var handleMethod = handler.GetType().GetMethod("Handle", new[] { command.GetType() }); - var response = (CommandResponse)handleMethod.Invoke(handler, new object[] { command }); + var response = getResponse(); if (response == null) { @@ -202,5 +226,19 @@ private CommandResponse Process(ICommand command) return response; } + + private CommandResponse GetCommandResponse(ICommand command) + { + var handler = _handlerResolver.ResolveHandler(command, typeof(ICommandHandler<>)); + var handleMethod = handler.GetType().GetMethod("Handle", new[] { command.GetType() }); + return (CommandResponse)handleMethod.Invoke(handler, new object[] { command }); + } + + private CommandResponse GetSequenceCommandResponse(ICommand command, CommandResponse previousStepResponse) + { + var handler = _handlerResolver.ResolveHandler(command, typeof(ISequenceCommandHandler<>)); + var handleMethod = handler.GetType().GetMethod("Handle", new[] { command.GetType(), typeof(CommandResponse) }); + return (CommandResponse)handleMethod.Invoke(handler, new object[] { command, previousStepResponse }); + } } } diff --git a/src/Kledex/Commands/ICommandSender.cs b/src/Kledex/Commands/ICommandSender.cs index ff60442d..559a137c 100644 --- a/src/Kledex/Commands/ICommandSender.cs +++ b/src/Kledex/Commands/ICommandSender.cs @@ -16,7 +16,7 @@ public interface ICommandSender Task SendAsync(ICommand command); /// - /// Sends the specified commands asynchronously. + /// Sends the specified sequence command asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The sequence command. @@ -31,7 +31,7 @@ public interface ICommandSender Task SendAsync(ICommand command); /// - /// Sends the specified commands asynchronously. + /// Sends the specified sequence command asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The type of the result. @@ -46,6 +46,13 @@ public interface ICommandSender /// The command. void Send(ICommand command); + /// + /// Sends the specified sequence command. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. + /// + /// The sequence command. + void Send(ISequenceCommand sequenceCommand); + /// /// Sends the specified command. /// The command handler must implement Kledex.Commands.ICommandHandler<TCommand>. @@ -53,5 +60,14 @@ public interface ICommandSender /// The command. /// A custom object set as result in the command hadler response. TResult Send(ICommand command); + + /// + /// Sends the sequence specified sequence command. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. + /// + /// The type of the result. + /// The sequence command. + /// A custom object set as result in the command hadler response. + TResult Send(ISequenceCommand sequenceCommand); } } diff --git a/src/Kledex/Commands/ISequenceCommandHandler.cs b/src/Kledex/Commands/ISequenceCommandHandler.cs new file mode 100644 index 00000000..b8a076dc --- /dev/null +++ b/src/Kledex/Commands/ISequenceCommandHandler.cs @@ -0,0 +1,7 @@ +namespace Kledex.Commands +{ + public interface ISequenceCommandHandler where TCommand : ICommand + { + CommandResponse Handle(TCommand command, CommandResponse previousStepResponse); + } +} diff --git a/src/Kledex/Dispatcher.cs b/src/Kledex/Dispatcher.cs index 47e4b4f0..7a82d2b2 100644 --- a/src/Kledex/Dispatcher.cs +++ b/src/Kledex/Dispatcher.cs @@ -80,12 +80,24 @@ public void Send(ICommand command) _commandSender.Send(command); } + /// + public void Send(ISequenceCommand sequenceCommand) + { + _commandSender.SendAsync(sequenceCommand); + } + /// public TResult Send(ICommand command) { return _commandSender.Send(command); } + /// + public TResult Send(ISequenceCommand sequenceCommand) + { + return _commandSender.Send(sequenceCommand); + } + /// public void Publish(TEvent @event) where TEvent : IEvent diff --git a/src/Kledex/IDispatcher.cs b/src/Kledex/IDispatcher.cs index 2cc6b23d..27564585 100644 --- a/src/Kledex/IDispatcher.cs +++ b/src/Kledex/IDispatcher.cs @@ -19,7 +19,7 @@ public interface IDispatcher Task SendAsync(ICommand command); /// - /// Sends the specified commands asynchronously. + /// Sends the specified sequence command asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The sequence command. @@ -34,7 +34,7 @@ public interface IDispatcher Task SendAsync(ICommand command); /// - /// Sends the specified commands asynchronously. + /// Sends the specified sequence command asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The type of the result. @@ -76,6 +76,13 @@ Task DispatchBusMessageAsync(TMessage message) /// The command. void Send(ICommand command); + /// + /// Sends the specified sequence command. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. + /// + /// The sequence command. + void Send(ISequenceCommand sequenceCommand); + /// /// Sends the specified command. /// The command handler must implement Kledex.Commands.ICommandHandler<TCommand>. @@ -84,6 +91,15 @@ Task DispatchBusMessageAsync(TMessage message) /// A custom object set as result in the command hadler response. TResult Send(ICommand command); + /// + /// Sends the sequence specified sequence command. + /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. + /// + /// The type of the result. + /// The sequence command. + /// A custom object set as result in the command hadler response. + TResult Send(ISequenceCommand sequenceCommand); + /// /// Publishes the specified event. /// The event handler must implement Kledex.Events.IEventHandler<TEvent>. From ebd2e0bfdfc3db17fc5408c3530093e2fcfb229d Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 15:36:11 +0000 Subject: [PATCH 10/22] Add dispatcher tests --- src/Kledex/Commands/SequenceCommand.cs | 2 +- src/Kledex/Dispatcher.cs | 2 +- test/Kledex.Tests/DispatcherTests.cs | 59 +++++++++++++++++-- .../Fakes/SampleSequenceCommand.cs | 8 +++ 4 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 test/Kledex.Tests/Fakes/SampleSequenceCommand.cs diff --git a/src/Kledex/Commands/SequenceCommand.cs b/src/Kledex/Commands/SequenceCommand.cs index 85c3071e..06eca541 100644 --- a/src/Kledex/Commands/SequenceCommand.cs +++ b/src/Kledex/Commands/SequenceCommand.cs @@ -9,7 +9,7 @@ public abstract class SequenceCommand : ISequenceCommand public ReadOnlyCollection Commands => _commands.AsReadOnly(); /// - /// Adds the command to the new command sequence collection. + /// Adds the command to the sequence collection. /// /// The command. protected void AddCommand(ICommand command) diff --git a/src/Kledex/Dispatcher.cs b/src/Kledex/Dispatcher.cs index 7a82d2b2..75438e77 100644 --- a/src/Kledex/Dispatcher.cs +++ b/src/Kledex/Dispatcher.cs @@ -83,7 +83,7 @@ public void Send(ICommand command) /// public void Send(ISequenceCommand sequenceCommand) { - _commandSender.SendAsync(sequenceCommand); + _commandSender.Send(sequenceCommand); } /// diff --git a/test/Kledex.Tests/DispatcherTests.cs b/test/Kledex.Tests/DispatcherTests.cs index 0976938c..03fe2c2e 100644 --- a/test/Kledex.Tests/DispatcherTests.cs +++ b/test/Kledex.Tests/DispatcherTests.cs @@ -19,29 +19,34 @@ public class DispatcherTests private Mock _queryDispatcher; private Mock _busMessageDispatcher; - private CreateSomething _createSomething; private SomethingCreated _somethingCreated; private GetSomething _getSomething; private Something _something; private CreateAggregate _createAggregate; private CreateAggregateBusMessage _createAggregateBusMessage; + private SampleSequenceCommand _sampleSequenceCommand; [SetUp] public void SetUp() { - _createSomething = new CreateSomething(); _somethingCreated = new SomethingCreated(); _getSomething = new GetSomething(); _something = new Something(); _createAggregate = new CreateAggregate(); _createAggregateBusMessage = new CreateAggregateBusMessage(); + _sampleSequenceCommand = new SampleSequenceCommand(); _commandSender = new Mock(); _commandSender - .Setup(x => x.SendAsync((IDomainCommand)_createAggregate)) + .Setup(x => x.SendAsync(_createAggregate)) .Returns(Task.CompletedTask); _commandSender - .Setup(x => x.Send((IDomainCommand)_createAggregate)); + .Setup(x => x.Send(_createAggregate)); + _commandSender + .Setup(x => x.SendAsync(_sampleSequenceCommand)) + .Returns(Task.CompletedTask); + _commandSender + .Setup(x => x.Send(_sampleSequenceCommand)); _eventPublisher = new Mock(); _eventPublisher @@ -70,19 +75,61 @@ public void SetUp() } [Test] - public async Task SendsCommandWithAggregateAsync() + public async Task SendsCommandAsync() { await _sut.SendAsync(_createAggregate); _commandSender.Verify(x => x.SendAsync(_createAggregate), Times.Once); } [Test] - public void SendsCommandWithAggregate() + public async Task SendsCommandWithResultAsync() + { + await _sut.SendAsync(_createAggregate); + _commandSender.Verify(x => x.SendAsync(_createAggregate), Times.Once); + } + + [Test] + public async Task SendsSequenceCommandAsync() + { + await _sut.SendAsync(_sampleSequenceCommand); + _commandSender.Verify(x => x.SendAsync(_sampleSequenceCommand), Times.Once); + } + + [Test] + public async Task SendsSequenceCommandWithResultAsync() + { + await _sut.SendAsync(_sampleSequenceCommand); + _commandSender.Verify(x => x.SendAsync(_sampleSequenceCommand), Times.Once); + } + + [Test] + public void SendsCommand() { _sut.Send(_createAggregate); _commandSender.Verify(x => x.Send(_createAggregate), Times.Once); } + [Test] + public void SendsCommandWithResult() + { + _sut.Send(_createAggregate); + _commandSender.Verify(x => x.Send(_createAggregate), Times.Once); + } + + [Test] + public void SendsSequenceCommand() + { + _sut.Send(_sampleSequenceCommand); + _commandSender.Verify(x => x.Send(_sampleSequenceCommand), Times.Once); + } + + [Test] + public void SendsSequenceCommandWithResult() + { + _sut.Send(_sampleSequenceCommand); + _commandSender.Verify(x => x.Send(_sampleSequenceCommand), Times.Once); + } + [Test] public async Task PublishesEventAsync() { diff --git a/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs b/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs new file mode 100644 index 00000000..6a2e9f33 --- /dev/null +++ b/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs @@ -0,0 +1,8 @@ +using Kledex.Commands; + +namespace Kledex.Tests.Fakes +{ + public class SampleSequenceCommand : SequenceCommand + { + } +} From 534534e9348a5e98a72ecf09306010f2f5dd3ccd Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 16:07:10 +0000 Subject: [PATCH 11/22] Add command sender tests --- .../Commands/CommandSenderAsyncTests.cs | 31 +++++++++++++++++-- .../Commands/CommandSenderTests.cs | 31 +++++++++++++++++-- .../Fakes/SampleSequenceCommand.cs | 4 +++ 3 files changed, 62 insertions(+), 4 deletions(-) diff --git a/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs b/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs index d6a4fac3..6a52867b 100644 --- a/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs +++ b/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs @@ -27,6 +27,7 @@ public class CommandSenderAsyncTests private Mock> _commandHandlerAsync; private Mock> _domainCommandHandlerAsync; + private Mock> _sequenceCommandHandlerAsync; private Mock> _optionsMock; private CreateSomething _createSomething; @@ -39,6 +40,8 @@ public class CommandSenderAsyncTests private AggregateCreated _aggregateCreatedConcrete; private Aggregate _aggregate; + private SampleSequenceCommand _sampleSequenceCommand; + private CommandResponse _commandResponse; private CommandResponse _domainCommandResponse; @@ -55,8 +58,10 @@ public void SetUp() _aggregate = new Aggregate(); _aggregateCreated = (AggregateCreated)_aggregate.Events[0]; - _commandResponse = new CommandResponse { Events = _events }; - _domainCommandResponse = new CommandResponse { Events = _aggregate.Events }; + _sampleSequenceCommand = new SampleSequenceCommand(); + + _commandResponse = new CommandResponse { Events = _events, Result = "Result" }; + _domainCommandResponse = new CommandResponse { Events = _aggregate.Events, Result = "Result" }; _eventPublisher = new Mock(); _eventPublisher @@ -91,6 +96,11 @@ public void SetUp() .Setup(x => x.HandleAsync(_createAggregate)) .ReturnsAsync(_domainCommandResponse); + _sequenceCommandHandlerAsync = new Mock>(); + _sequenceCommandHandlerAsync + .Setup(x => x.HandleAsync(It.IsAny(), It.IsAny())) + .ReturnsAsync(It.IsAny()); + _handlerResolver = new Mock(); _handlerResolver .Setup(x => x.ResolveHandler(_createSomething, typeof(ICommandHandlerAsync<>))) @@ -98,6 +108,9 @@ public void SetUp() _handlerResolver .Setup(x => x.ResolveHandler(_createAggregate, typeof(ICommandHandlerAsync<>))) .Returns(_domainCommandHandlerAsync.Object); + _handlerResolver + .Setup(x => x.ResolveHandler(It.IsAny(), typeof(ISequenceCommandHandlerAsync<>))) + .Returns(_sequenceCommandHandlerAsync.Object); _optionsMock = new Mock>(); _optionsMock @@ -141,6 +154,13 @@ public async Task SendAsync_HandlesDomainCommand() _domainCommandHandlerAsync.Verify(x => x.HandleAsync(_createAggregate), Times.Once); } + [Test] + public async Task SendAsync_HandlesCommand_InSequenceCommand() + { + await _sut.SendAsync(_sampleSequenceCommand); + _sequenceCommandHandlerAsync.Verify(x => x.HandleAsync(It.IsAny(), It.IsAny()), Times.Once); + } + [Test] public async Task SendAsync_SavesEvents() { @@ -180,5 +200,12 @@ public async Task SendAsync_NotPublishesEvents_WhenSetInCommand() await _sut.SendAsync(_createAggregate); _eventPublisher.Verify(x => x.PublishAsync(_aggregateCreatedConcrete), Times.Never); } + + [Test] + public async Task SendAsyncWithResult_ReturnsResult() + { + var actual = await _sut.SendAsync(_createSomething); + Assert.AreEqual("Result", actual); + } } } diff --git a/test/Kledex.Tests/Commands/CommandSenderTests.cs b/test/Kledex.Tests/Commands/CommandSenderTests.cs index 7b99d67b..c63f8de1 100644 --- a/test/Kledex.Tests/Commands/CommandSenderTests.cs +++ b/test/Kledex.Tests/Commands/CommandSenderTests.cs @@ -26,6 +26,7 @@ public class CommandSenderTests private Mock> _commandHandler; private Mock> _domainCommandHandler; + private Mock> _sequenceCommandHandler; private Mock> _optionsMock; private CreateSomething _createSomething; @@ -38,6 +39,8 @@ public class CommandSenderTests private AggregateCreated _aggregateCreatedConcrete; private Aggregate _aggregate; + private SampleSequenceCommand _sampleSequenceCommand; + private CommandResponse _commandResponse; private CommandResponse _domainCommandResponse; @@ -54,8 +57,10 @@ public void SetUp() _aggregate = new Aggregate(); _aggregateCreated = (AggregateCreated)_aggregate.Events[0]; - _commandResponse = new CommandResponse { Events = _events }; - _domainCommandResponse = new CommandResponse { Events = _aggregate.Events }; + _sampleSequenceCommand = new SampleSequenceCommand(); + + _commandResponse = new CommandResponse { Events = _events, Result = "Result" }; + _domainCommandResponse = new CommandResponse { Events = _aggregate.Events, Result = "Result" }; _eventPublisher = new Mock(); _eventPublisher @@ -87,6 +92,11 @@ public void SetUp() .Setup(x => x.Handle(_createAggregate)) .Returns(_domainCommandResponse); + _sequenceCommandHandler = new Mock>(); + _sequenceCommandHandler + .Setup(x => x.Handle(It.IsAny(), It.IsAny())) + .Returns(It.IsAny()); + _handlerResolver = new Mock(); _handlerResolver .Setup(x => x.ResolveHandler(_createSomething, typeof(ICommandHandler<>))) @@ -94,6 +104,9 @@ public void SetUp() _handlerResolver .Setup(x => x.ResolveHandler(_createAggregate, typeof(ICommandHandler<>))) .Returns(_domainCommandHandler.Object); + _handlerResolver + .Setup(x => x.ResolveHandler(It.IsAny(), typeof(ISequenceCommandHandler<>))) + .Returns(_sequenceCommandHandler.Object); _optionsMock = new Mock>(); _optionsMock @@ -137,6 +150,13 @@ public void Send_HandlesDomainCommand() _domainCommandHandler.Verify(x => x.Handle(_createAggregate), Times.Once); } + [Test] + public void Send_HandlesCommand_InSequenceCommand() + { + _sut.Send(_sampleSequenceCommand); + _sequenceCommandHandler.Verify(x => x.Handle(It.IsAny(), It.IsAny()), Times.Once); + } + [Test] public void Send_SavesEvents() { @@ -176,5 +196,12 @@ public void Send_NotPublishesEvents_WhenSetInCommand() _sut.Send(_createAggregate); _eventPublisher.Verify(x => x.Publish(_aggregateCreatedConcrete), Times.Never); } + + [Test] + public void SendWithResult_ReturnsResult() + { + var actual = _sut.Send(_createSomething); + Assert.AreEqual("Result", actual); + } } } diff --git a/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs b/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs index 6a2e9f33..5e6c6e8c 100644 --- a/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs +++ b/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs @@ -4,5 +4,9 @@ namespace Kledex.Tests.Fakes { public class SampleSequenceCommand : SequenceCommand { + public SampleSequenceCommand() + { + AddCommand(new CreateSomething()); + } } } From 3068b1e3ecf9f8f93f2e48b2ff04499da4c6bd13 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 17:51:48 +0000 Subject: [PATCH 12/22] Rename SequenceCommand to CommandSequence --- ...nceCommand.cs => SampleCommandSequence.cs} | 8 ++--- .../Kledex.Sample.CommandSequence/Program.cs | 2 +- src/Kledex/Commands/CommandSender.cs | 12 +++---- ...{SequenceCommand.cs => CommandSequence.cs} | 2 +- src/Kledex/Commands/ICommandSender.cs | 8 ++--- ...SequenceCommand.cs => ICommandSequence.cs} | 2 +- src/Kledex/Dispatcher.cs | 8 ++--- src/Kledex/IDispatcher.cs | 8 ++--- .../Commands/CommandSenderAsyncTests.cs | 8 ++--- .../Commands/CommandSenderTests.cs | 8 ++--- test/Kledex.Tests/DispatcherTests.cs | 32 +++++++++---------- .../Fakes/SampleSequenceCommand.cs | 4 +-- 12 files changed, 50 insertions(+), 52 deletions(-) rename samples/Kledex.Sample.CommandSequence/Commands/{SampleSequenceCommand.cs => SampleCommandSequence.cs} (52%) rename src/Kledex/Commands/{SequenceCommand.cs => CommandSequence.cs} (89%) rename src/Kledex/Commands/{ISequenceCommand.cs => ICommandSequence.cs} (78%) diff --git a/samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs b/samples/Kledex.Sample.CommandSequence/Commands/SampleCommandSequence.cs similarity index 52% rename from samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs rename to samples/Kledex.Sample.CommandSequence/Commands/SampleCommandSequence.cs index e1bc2e98..e4325b19 100644 --- a/samples/Kledex.Sample.CommandSequence/Commands/SampleSequenceCommand.cs +++ b/samples/Kledex.Sample.CommandSequence/Commands/SampleCommandSequence.cs @@ -1,10 +1,8 @@ -using Kledex.Commands; - -namespace Kledex.Sample.CommandSequence.Commands +namespace Kledex.Sample.CommandSequence.Commands { - public class SampleSequenceCommand : SequenceCommand + public class SampleCommandSequence : Kledex.Commands.CommandSequence { - public SampleSequenceCommand() + public SampleCommandSequence() { AddCommand(new FirstCommand { Name = "My Name" }); AddCommand(new SecondCommand()); diff --git a/samples/Kledex.Sample.CommandSequence/Program.cs b/samples/Kledex.Sample.CommandSequence/Program.cs index 1852e508..c5441400 100644 --- a/samples/Kledex.Sample.CommandSequence/Program.cs +++ b/samples/Kledex.Sample.CommandSequence/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) var dispatcher = serviceProvider.GetService(); var result = dispatcher - .SendAsync(new SampleSequenceCommand()) + .SendAsync(new SampleCommandSequence()) .GetAwaiter().GetResult(); Console.WriteLine($"Final result: {result}"); diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index 658ff536..73e673d1 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -54,7 +54,7 @@ public async Task SendAsync(ICommand command) } /// - public Task SendAsync(ISequenceCommand sequenceCommand) + public Task SendAsync(ICommandSequence sequenceCommand) { return ProcessSequenceCommandAsync(sequenceCommand); } @@ -67,13 +67,13 @@ public async Task SendAsync(ICommand command) } /// - public async Task SendAsync(ISequenceCommand sequenceCommand) + public async Task SendAsync(ICommandSequence sequenceCommand) { var lastStepReponse = await ProcessSequenceCommandAsync(sequenceCommand); return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - private async Task ProcessSequenceCommandAsync(ISequenceCommand sequenceCommand) + private async Task ProcessSequenceCommandAsync(ICommandSequence sequenceCommand) { CommandResponse lastStepResponse = null; @@ -151,7 +151,7 @@ public void Send(ICommand command) } /// - public void Send(ISequenceCommand sequenceCommand) + public void Send(ICommandSequence sequenceCommand) { ProcessSequenceCommand(sequenceCommand); } @@ -164,13 +164,13 @@ public TResult Send(ICommand command) } /// - public TResult Send(ISequenceCommand sequenceCommand) + public TResult Send(ICommandSequence sequenceCommand) { var lastStepReponse = ProcessSequenceCommand(sequenceCommand); return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - private CommandResponse ProcessSequenceCommand(ISequenceCommand sequenceCommand) + private CommandResponse ProcessSequenceCommand(ICommandSequence sequenceCommand) { CommandResponse lastStepResponse = null; diff --git a/src/Kledex/Commands/SequenceCommand.cs b/src/Kledex/Commands/CommandSequence.cs similarity index 89% rename from src/Kledex/Commands/SequenceCommand.cs rename to src/Kledex/Commands/CommandSequence.cs index 06eca541..beb55c7b 100644 --- a/src/Kledex/Commands/SequenceCommand.cs +++ b/src/Kledex/Commands/CommandSequence.cs @@ -3,7 +3,7 @@ namespace Kledex.Commands { - public abstract class SequenceCommand : ISequenceCommand + public abstract class CommandSequence : ICommandSequence { private readonly List _commands = new List(); public ReadOnlyCollection Commands => _commands.AsReadOnly(); diff --git a/src/Kledex/Commands/ICommandSender.cs b/src/Kledex/Commands/ICommandSender.cs index 559a137c..3dac8fe3 100644 --- a/src/Kledex/Commands/ICommandSender.cs +++ b/src/Kledex/Commands/ICommandSender.cs @@ -20,7 +20,7 @@ public interface ICommandSender /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The sequence command. - Task SendAsync(ISequenceCommand sequenceCommand); + Task SendAsync(ICommandSequence sequenceCommand); /// /// Sends the specified command asynchronously. @@ -37,7 +37,7 @@ public interface ICommandSender /// The type of the result. /// The sequence command. /// A custom object set as result in the command hadler response. - Task SendAsync(ISequenceCommand sequenceCommand); + Task SendAsync(ICommandSequence sequenceCommand); /// /// Sends the specified command. @@ -51,7 +51,7 @@ public interface ICommandSender /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. /// /// The sequence command. - void Send(ISequenceCommand sequenceCommand); + void Send(ICommandSequence sequenceCommand); /// /// Sends the specified command. @@ -68,6 +68,6 @@ public interface ICommandSender /// The type of the result. /// The sequence command. /// A custom object set as result in the command hadler response. - TResult Send(ISequenceCommand sequenceCommand); + TResult Send(ICommandSequence sequenceCommand); } } diff --git a/src/Kledex/Commands/ISequenceCommand.cs b/src/Kledex/Commands/ICommandSequence.cs similarity index 78% rename from src/Kledex/Commands/ISequenceCommand.cs rename to src/Kledex/Commands/ICommandSequence.cs index 758c2a68..9b9fd14e 100644 --- a/src/Kledex/Commands/ISequenceCommand.cs +++ b/src/Kledex/Commands/ICommandSequence.cs @@ -2,7 +2,7 @@ namespace Kledex.Commands { - public interface ISequenceCommand + public interface ICommandSequence { ReadOnlyCollection Commands { get; } } diff --git a/src/Kledex/Dispatcher.cs b/src/Kledex/Dispatcher.cs index 75438e77..b484607f 100644 --- a/src/Kledex/Dispatcher.cs +++ b/src/Kledex/Dispatcher.cs @@ -37,7 +37,7 @@ public Task SendAsync(ICommand command) } /// - public Task SendAsync(ISequenceCommand sequenceCommand) + public Task SendAsync(ICommandSequence sequenceCommand) { return _commandSender.SendAsync(sequenceCommand); } @@ -49,7 +49,7 @@ public Task SendAsync(ICommand command) } /// - public Task SendAsync(ISequenceCommand sequenceCommand) + public Task SendAsync(ICommandSequence sequenceCommand) { return _commandSender.SendAsync(sequenceCommand); } @@ -81,7 +81,7 @@ public void Send(ICommand command) } /// - public void Send(ISequenceCommand sequenceCommand) + public void Send(ICommandSequence sequenceCommand) { _commandSender.Send(sequenceCommand); } @@ -93,7 +93,7 @@ public TResult Send(ICommand command) } /// - public TResult Send(ISequenceCommand sequenceCommand) + public TResult Send(ICommandSequence sequenceCommand) { return _commandSender.Send(sequenceCommand); } diff --git a/src/Kledex/IDispatcher.cs b/src/Kledex/IDispatcher.cs index 27564585..179ea0ef 100644 --- a/src/Kledex/IDispatcher.cs +++ b/src/Kledex/IDispatcher.cs @@ -23,7 +23,7 @@ public interface IDispatcher /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The sequence command. - Task SendAsync(ISequenceCommand sequenceCommand); + Task SendAsync(ICommandSequence sequenceCommand); /// /// Sends the specified command asynchronously. @@ -40,7 +40,7 @@ public interface IDispatcher /// The type of the result. /// The sequence command. /// A custom object set as result in the command hadler response. - Task SendAsync(ISequenceCommand sequenceCommand); + Task SendAsync(ICommandSequence sequenceCommand); /// /// Asynchronously publishes the specified event. @@ -81,7 +81,7 @@ Task DispatchBusMessageAsync(TMessage message) /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. /// /// The sequence command. - void Send(ISequenceCommand sequenceCommand); + void Send(ICommandSequence sequenceCommand); /// /// Sends the specified command. @@ -98,7 +98,7 @@ Task DispatchBusMessageAsync(TMessage message) /// The type of the result. /// The sequence command. /// A custom object set as result in the command hadler response. - TResult Send(ISequenceCommand sequenceCommand); + TResult Send(ICommandSequence sequenceCommand); /// /// Publishes the specified event. diff --git a/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs b/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs index 6a52867b..ea12026d 100644 --- a/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs +++ b/test/Kledex.Tests/Commands/CommandSenderAsyncTests.cs @@ -40,7 +40,7 @@ public class CommandSenderAsyncTests private AggregateCreated _aggregateCreatedConcrete; private Aggregate _aggregate; - private SampleSequenceCommand _sampleSequenceCommand; + private sampleCommandSequence _sampleCommandSequence; private CommandResponse _commandResponse; private CommandResponse _domainCommandResponse; @@ -58,7 +58,7 @@ public void SetUp() _aggregate = new Aggregate(); _aggregateCreated = (AggregateCreated)_aggregate.Events[0]; - _sampleSequenceCommand = new SampleSequenceCommand(); + _sampleCommandSequence = new sampleCommandSequence(); _commandResponse = new CommandResponse { Events = _events, Result = "Result" }; _domainCommandResponse = new CommandResponse { Events = _aggregate.Events, Result = "Result" }; @@ -155,9 +155,9 @@ public async Task SendAsync_HandlesDomainCommand() } [Test] - public async Task SendAsync_HandlesCommand_InSequenceCommand() + public async Task SendAsync_HandlesCommand_InCommandSequence() { - await _sut.SendAsync(_sampleSequenceCommand); + await _sut.SendAsync(_sampleCommandSequence); _sequenceCommandHandlerAsync.Verify(x => x.HandleAsync(It.IsAny(), It.IsAny()), Times.Once); } diff --git a/test/Kledex.Tests/Commands/CommandSenderTests.cs b/test/Kledex.Tests/Commands/CommandSenderTests.cs index c63f8de1..056da6a4 100644 --- a/test/Kledex.Tests/Commands/CommandSenderTests.cs +++ b/test/Kledex.Tests/Commands/CommandSenderTests.cs @@ -39,7 +39,7 @@ public class CommandSenderTests private AggregateCreated _aggregateCreatedConcrete; private Aggregate _aggregate; - private SampleSequenceCommand _sampleSequenceCommand; + private sampleCommandSequence _sampleCommandSequence; private CommandResponse _commandResponse; private CommandResponse _domainCommandResponse; @@ -57,7 +57,7 @@ public void SetUp() _aggregate = new Aggregate(); _aggregateCreated = (AggregateCreated)_aggregate.Events[0]; - _sampleSequenceCommand = new SampleSequenceCommand(); + _sampleCommandSequence = new sampleCommandSequence(); _commandResponse = new CommandResponse { Events = _events, Result = "Result" }; _domainCommandResponse = new CommandResponse { Events = _aggregate.Events, Result = "Result" }; @@ -151,9 +151,9 @@ public void Send_HandlesDomainCommand() } [Test] - public void Send_HandlesCommand_InSequenceCommand() + public void Send_HandlesCommand_InCommandSequence() { - _sut.Send(_sampleSequenceCommand); + _sut.Send(_sampleCommandSequence); _sequenceCommandHandler.Verify(x => x.Handle(It.IsAny(), It.IsAny()), Times.Once); } diff --git a/test/Kledex.Tests/DispatcherTests.cs b/test/Kledex.Tests/DispatcherTests.cs index 03fe2c2e..874e9dc5 100644 --- a/test/Kledex.Tests/DispatcherTests.cs +++ b/test/Kledex.Tests/DispatcherTests.cs @@ -24,7 +24,7 @@ public class DispatcherTests private Something _something; private CreateAggregate _createAggregate; private CreateAggregateBusMessage _createAggregateBusMessage; - private SampleSequenceCommand _sampleSequenceCommand; + private sampleCommandSequence _sampleCommandSequence; [SetUp] public void SetUp() @@ -34,7 +34,7 @@ public void SetUp() _something = new Something(); _createAggregate = new CreateAggregate(); _createAggregateBusMessage = new CreateAggregateBusMessage(); - _sampleSequenceCommand = new SampleSequenceCommand(); + _sampleCommandSequence = new sampleCommandSequence(); _commandSender = new Mock(); _commandSender @@ -43,10 +43,10 @@ public void SetUp() _commandSender .Setup(x => x.Send(_createAggregate)); _commandSender - .Setup(x => x.SendAsync(_sampleSequenceCommand)) + .Setup(x => x.SendAsync(_sampleCommandSequence)) .Returns(Task.CompletedTask); _commandSender - .Setup(x => x.Send(_sampleSequenceCommand)); + .Setup(x => x.Send(_sampleCommandSequence)); _eventPublisher = new Mock(); _eventPublisher @@ -89,17 +89,17 @@ public async Task SendsCommandWithResultAsync() } [Test] - public async Task SendsSequenceCommandAsync() + public async Task SendsCommandSequenceAsync() { - await _sut.SendAsync(_sampleSequenceCommand); - _commandSender.Verify(x => x.SendAsync(_sampleSequenceCommand), Times.Once); + await _sut.SendAsync(_sampleCommandSequence); + _commandSender.Verify(x => x.SendAsync(_sampleCommandSequence), Times.Once); } [Test] - public async Task SendsSequenceCommandWithResultAsync() + public async Task SendsCommandSequenceWithResultAsync() { - await _sut.SendAsync(_sampleSequenceCommand); - _commandSender.Verify(x => x.SendAsync(_sampleSequenceCommand), Times.Once); + await _sut.SendAsync(_sampleCommandSequence); + _commandSender.Verify(x => x.SendAsync(_sampleCommandSequence), Times.Once); } [Test] @@ -117,17 +117,17 @@ public void SendsCommandWithResult() } [Test] - public void SendsSequenceCommand() + public void SendsCommandSequence() { - _sut.Send(_sampleSequenceCommand); - _commandSender.Verify(x => x.Send(_sampleSequenceCommand), Times.Once); + _sut.Send(_sampleCommandSequence); + _commandSender.Verify(x => x.Send(_sampleCommandSequence), Times.Once); } [Test] - public void SendsSequenceCommandWithResult() + public void SendsCommandSequenceWithResult() { - _sut.Send(_sampleSequenceCommand); - _commandSender.Verify(x => x.Send(_sampleSequenceCommand), Times.Once); + _sut.Send(_sampleCommandSequence); + _commandSender.Verify(x => x.Send(_sampleCommandSequence), Times.Once); } [Test] diff --git a/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs b/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs index 5e6c6e8c..3269234d 100644 --- a/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs +++ b/test/Kledex.Tests/Fakes/SampleSequenceCommand.cs @@ -2,9 +2,9 @@ namespace Kledex.Tests.Fakes { - public class SampleSequenceCommand : SequenceCommand + public class sampleCommandSequence : CommandSequence { - public SampleSequenceCommand() + public sampleCommandSequence() { AddCommand(new CreateSomething()); } From e2d5aa2c709f028e9d3f3102021bdd4686e855ea Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 21:01:12 +0000 Subject: [PATCH 13/22] Update docs --- Kledex.sln | 9 +++++---- docs/Basics.md | 11 ++++++----- docs/{Caching.md => Caching-Queries.md} | 4 ++++ docs/Command-Sequence.md | 10 ++++++++++ docs/{Validation.md => Command-Validation.md} | 7 +++++++ docs/Commands.md | 7 ++++++- docs/Domain-Commands.md | 6 ++++++ docs/Domain.md | 5 ----- docs/Queries.md | 4 +++- docs/UI.md | 2 +- docs/With-Event-Sourcing.md | 7 ++++++- docs/Without-Event-Sourcing.md | 7 ++++++- docs/index.md | 7 ++++--- 13 files changed, 64 insertions(+), 22 deletions(-) rename docs/{Caching.md => Caching-Queries.md} (97%) create mode 100644 docs/Command-Sequence.md rename docs/{Validation.md => Command-Validation.md} (89%) create mode 100644 docs/Domain-Commands.md delete mode 100644 docs/Domain.md diff --git a/Kledex.sln b/Kledex.sln index c20fe484..f3b84ee7 100644 --- a/Kledex.sln +++ b/Kledex.sln @@ -67,10 +67,12 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{1282A8FB-D7A5-4562-8B9B-1C27B83EE580}" ProjectSection(SolutionItems) = preProject docs\Basics.md = docs\Basics.md - docs\Caching.md = docs\Caching.md + docs\Caching-Queries.md = docs\Caching-Queries.md + docs\Command-Sequence.md = docs\Command-Sequence.md + docs\Command-Validation.md = docs\Command-Validation.md docs\Commands.md = docs\Commands.md docs\Configuration.md = docs\Configuration.md - docs\Domain.md = docs\Domain.md + docs\Domain-Commands.md = docs\Domain-Commands.md docs\Events.md = docs\Events.md docs\index.md = docs\index.md docs\Installation.md = docs\Installation.md @@ -80,7 +82,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{1282A8FB-D docs\Release-Notes.md = docs\Release-Notes.md docs\Samples.md = docs\Samples.md docs\UI.md = docs\UI.md - docs\Validation.md = docs\Validation.md docs\With-Event-Sourcing.md = docs\With-Event-Sourcing.md docs\Without-Event-Sourcing.md = docs\Without-Event-Sourcing.md EndProjectSection @@ -94,7 +95,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "img", "img", "{05D6EE8E-C35 docs\assets\img\SendCommandFlow.svg = docs\assets\img\SendCommandFlow.svg EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kledex.Sample.CommandSequence", "samples\Kledex.Sample.CommandSequence\Kledex.Sample.CommandSequence.csproj", "{6DB19E10-1942-44CC-B592-01A34CEE8C77}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Kledex.Sample.CommandSequence", "samples\Kledex.Sample.CommandSequence\Kledex.Sample.CommandSequence.csproj", "{6DB19E10-1942-44CC-B592-01A34CEE8C77}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/docs/Basics.md b/docs/Basics.md index 68e64a5f..80227594 100644 --- a/docs/Basics.md +++ b/docs/Basics.md @@ -19,11 +19,12 @@ It's also possible to use the following interfaces directly without going throug The following is the mapping between dispatcher methods and handlers: -| Method | Handler | -| --- | --- | -| SendAsync | ICommandHandlerAsync | -| PublishAsync | IEventHandlerAsync | -| GetResultAsync | IQueryHandlerAsync | +| Method | Parameter | Handler | +| --- | --- | --- | +| SendAsync | ICommand | ICommandHandlerAsync | +| SendAsync | ICommandSequence | ISequenceCommandHandlerAsync | +| PublishAsync | IEvent | IEventHandlerAsync | +| GetResultAsync | IQuery | IQueryHandlerAsync | ## Main Flow diff --git a/docs/Caching.md b/docs/Caching-Queries.md similarity index 97% rename from docs/Caching.md rename to docs/Caching-Queries.md index 385079e6..5cedc101 100644 --- a/docs/Caching.md +++ b/docs/Caching-Queries.md @@ -48,3 +48,7 @@ var something = await _dispatcher.GetResultAsync(query); ``` As you can see the only difference is in the query class. + +## Related + +- [Queries](Queries) diff --git a/docs/Command-Sequence.md b/docs/Command-Sequence.md new file mode 100644 index 00000000..568dbf38 --- /dev/null +++ b/docs/Command-Sequence.md @@ -0,0 +1,10 @@ +# Command Sequence + +Work in progress... + +## Related + +- [Commands](Commands) +- [Command Validation](Command-Validation) +- [Domain Commands With Event Sourcing](With-Event-Sourcing) +- [Domain Commands Without Event Sourcing](Without-Event-Sourcing) diff --git a/docs/Validation.md b/docs/Command-Validation.md similarity index 89% rename from docs/Validation.md rename to docs/Command-Validation.md index 57297663..57ed2aa5 100644 --- a/docs/Validation.md +++ b/docs/Command-Validation.md @@ -37,3 +37,10 @@ public CreateProductValidator() Kledex will automatically resolve and execute it. Note that the assemblies that contain the validators need to be registered the same way command, query and event handlers are as explained [here](Configuration#main). + +## Related + +- [Commands](Commands) +- [Command Sequence](Command-Sequence) +- [Domain Commands With Event Sourcing](With-Event-Sourcing) +- [Domain Commands Without Event Sourcing](Without-Event-Sourcing) diff --git a/docs/Commands.md b/docs/Commands.md index 8bba88e1..8eaf20c8 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -89,4 +89,9 @@ public class DoSomethingHandler : ICommandHandlerAsync Note the two optional properties can be set in the commands: **UserId** and **Source**. -It is possible to validate the command automatically before it is sent to the command handler. [Click here to know more](Validation). +## Related + +- [Command Validation](Command-Validation) +- [Command Sequence](Command-Sequence) +- [Domain Commands With Event Sourcing](With-Event-Sourcing) +- [Domain Commands Without Event Sourcing](Without-Event-Sourcing) diff --git a/docs/Domain-Commands.md b/docs/Domain-Commands.md new file mode 100644 index 00000000..09adcaa2 --- /dev/null +++ b/docs/Domain-Commands.md @@ -0,0 +1,6 @@ +# Domain Commands + +Working with your domain model: + +- [Domain Commands With Event Sourcing](With-Event-Sourcing) +- [Domain Commands Without Event Sourcing](Without-Event-Sourcing) diff --git a/docs/Domain.md b/docs/Domain.md deleted file mode 100644 index 2102d5b7..00000000 --- a/docs/Domain.md +++ /dev/null @@ -1,5 +0,0 @@ -# Domain - -Working with your domain model: -- [With Event Sourcing](With-Event-Sourcing) -- [Without Event Sourcing](Without-Event-Sourcing) diff --git a/docs/Queries.md b/docs/Queries.md index 6b5eb760..9d1b05fe 100644 --- a/docs/Queries.md +++ b/docs/Queries.md @@ -41,4 +41,6 @@ var query = new GetSomething { Id = 123 }; var something = await _dispatcher.GetResultAsync(query); ``` -It is possible to automatically cahce the result using one of the cache providers. [Click here to know more](Caching). +## Related + +- [Caching Queries](Caching-Queries). diff --git a/docs/UI.md b/docs/UI.md index 13b7d9a3..2f77de72 100644 --- a/docs/UI.md +++ b/docs/UI.md @@ -1,3 +1,3 @@ # UI -Work in progress... \ No newline at end of file +Work in progress... diff --git a/docs/With-Event-Sourcing.md b/docs/With-Event-Sourcing.md index 6cdc0d9c..94ccd6b2 100644 --- a/docs/With-Event-Sourcing.md +++ b/docs/With-Event-Sourcing.md @@ -185,4 +185,9 @@ await dispatcher.SendAsync(new UpdateProductTitle A new event is saved and the read model is updated using the event handler. Next time the aggregate is loaded from history using the repository, two events will be applied in order to recreate the current state. -It is possible to validate the command automatically before it is sent to the command handler. [Click here to know more](Validation). +## Related + +- [Commands](Commands) +- [Command Validation](Command-Validation) +- [Command Sequence](Command-Sequence) +- [Domain Commands Without Event Sourcing](Without-Event-Sourcing) diff --git a/docs/Without-Event-Sourcing.md b/docs/Without-Event-Sourcing.md index b4bf1819..56254eaa 100644 --- a/docs/Without-Event-Sourcing.md +++ b/docs/Without-Event-Sourcing.md @@ -99,4 +99,9 @@ The read model(s) can be created the same way we would have done using Event Sou The main difference here is that we are using a normal repository for saving our normalised data but at the same time we have the complete history of all the changes that happened to the domain (all events are automatically saved anyway by the framework). -It is possible to validate the command automatically before it is sent to the command handler. [Click here to know more](Validation). +## Related + +- [Commands](Commands) +- [Command Validation](Command-Validation) +- [Command Sequence](Command-Sequence) +- [Domain Commands With Event Sourcing](With-Event-Sourcing) diff --git a/docs/index.md b/docs/index.md index 01174f1c..480e627e 100644 --- a/docs/index.md +++ b/docs/index.md @@ -16,12 +16,13 @@ With Kledex you can automatically dispatch events to a message bus (Service Bus - [Commands](Commands) - [Events](Events) - [Queries](Queries) -- [Domain](Domain) +- [Domain Commands](Domain-Commands) - [With Event Sourcing](With-Event-Sourcing) - [Without Event Sourcing](Without-Event-Sourcing) +- [Command Validation](Command-Validation) +- [Command Sequence](Command-Sequence) - [Message Bus](Message-Bus) -- [Validation](Validation) -- [Caching](Caching) +- [Caching Queries](Caching-Queries) - [UI](UI) - [Providers](Providers) - [Samples](Samples) From e771cd988ffca68e6d8c2f95ed8247848601bc13 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 21:07:30 +0000 Subject: [PATCH 14/22] Update docs --- docs/Command-Validation.md | 2 +- docs/Commands.md | 4 ++-- docs/Queries.md | 2 +- docs/With-Event-Sourcing.md | 2 +- docs/Without-Event-Sourcing.md | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Command-Validation.md b/docs/Command-Validation.md index 57ed2aa5..2f51b603 100644 --- a/docs/Command-Validation.md +++ b/docs/Command-Validation.md @@ -1,4 +1,4 @@ -# Validation +# Command Validation Kledex can automatically call a validation service before a command is sent to the command handler. In order to do that a validation provider needs to be installed and configured as described [here](Configuration#validation). You can configure to validate all commands by setting the option _ValidateCommands_ to _true_ when registering the main package as described [here](Configuration#main) or on a case by case basis by setting the _Validate_ property to _true_ at the command level: diff --git a/docs/Commands.md b/docs/Commands.md index 8eaf20c8..839da3b5 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -1,6 +1,6 @@ # Commands -The dispatcher will automatically publish any events returned by the handler unless the property **PublishEvents** is set to _false_ on a global or a command level. +The dispatcher will automatically publish any events returned by the handler unless the property **PublishEvents** is set to _false_ on a global or a command level (see [Configuration](Configuration#main)). First, create a command that inherits from the **Command** class: @@ -87,7 +87,7 @@ public class DoSomethingHandler : ICommandHandlerAsync } ``` -Note the two optional properties can be set in the commands: **UserId** and **Source**. +Note that two optional properties can be set in the commands: **UserId** and **Source**. ## Related diff --git a/docs/Queries.md b/docs/Queries.md index 9d1b05fe..e4687a16 100644 --- a/docs/Queries.md +++ b/docs/Queries.md @@ -43,4 +43,4 @@ var something = await _dispatcher.GetResultAsync(query); ## Related -- [Caching Queries](Caching-Queries). +- [Caching Queries](Caching-Queries) diff --git a/docs/With-Event-Sourcing.md b/docs/With-Event-Sourcing.md index 94ccd6b2..e5e8cb24 100644 --- a/docs/With-Event-Sourcing.md +++ b/docs/With-Event-Sourcing.md @@ -1,4 +1,4 @@ -# With Event Sourcing +# Domain Commands With Event Sourcing By using the _SendAsync_ method, the dispatcher will automatically publish the events set in the response of the handler and save those events to the domain store (alongside aggregate and command). diff --git a/docs/Without-Event-Sourcing.md b/docs/Without-Event-Sourcing.md index 56254eaa..4ead78a3 100644 --- a/docs/Without-Event-Sourcing.md +++ b/docs/Without-Event-Sourcing.md @@ -1,4 +1,4 @@ -# Without Event Sourcing +# Domain Commands Without Event Sourcing Event Sourcing functionalities of Kledex can be used just as an advanced logging system for your domain. The single source of truth can be a SQL Server database with normalised data (or any other data provider) and use the read model for optimised queries or use just one data store altogether. From 0deba6d9ab37745e7a90e8b4c30a0399bd62c6fb Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 21:30:18 +0000 Subject: [PATCH 15/22] Update dispatcher and command sender --- src/Kledex/Commands/CommandSender.cs | 24 ++++++++++++------------ src/Kledex/Commands/ICommandSender.cs | 24 ++++++++++++------------ src/Kledex/Dispatcher.cs | 16 ++++++++-------- src/Kledex/IDispatcher.cs | 24 ++++++++++++------------ 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/Kledex/Commands/CommandSender.cs b/src/Kledex/Commands/CommandSender.cs index 73e673d1..5aab4f70 100644 --- a/src/Kledex/Commands/CommandSender.cs +++ b/src/Kledex/Commands/CommandSender.cs @@ -54,9 +54,9 @@ public async Task SendAsync(ICommand command) } /// - public Task SendAsync(ICommandSequence sequenceCommand) + public Task SendAsync(ICommandSequence commandSequence) { - return ProcessSequenceCommandAsync(sequenceCommand); + return ProcessCommandSequenceAsync(commandSequence); } /// @@ -67,17 +67,17 @@ public async Task SendAsync(ICommand command) } /// - public async Task SendAsync(ICommandSequence sequenceCommand) + public async Task SendAsync(ICommandSequence commandSequence) { - var lastStepReponse = await ProcessSequenceCommandAsync(sequenceCommand); + var lastStepReponse = await ProcessCommandSequenceAsync(commandSequence); return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - private async Task ProcessSequenceCommandAsync(ICommandSequence sequenceCommand) + private async Task ProcessCommandSequenceAsync(ICommandSequence commandSequence) { CommandResponse lastStepResponse = null; - foreach (var command in sequenceCommand.Commands) + foreach (var command in commandSequence.Commands) { var response = await ProcessAsync(command, () => GetSequenceCommandResponseAsync(command, lastStepResponse)); lastStepResponse = response; @@ -151,9 +151,9 @@ public void Send(ICommand command) } /// - public void Send(ICommandSequence sequenceCommand) + public void Send(ICommandSequence commandSequence) { - ProcessSequenceCommand(sequenceCommand); + ProcessSequenceCommand(commandSequence); } /// @@ -164,17 +164,17 @@ public TResult Send(ICommand command) } /// - public TResult Send(ICommandSequence sequenceCommand) + public TResult Send(ICommandSequence commandSequence) { - var lastStepReponse = ProcessSequenceCommand(sequenceCommand); + var lastStepReponse = ProcessSequenceCommand(commandSequence); return lastStepReponse?.Result != null ? (TResult)lastStepReponse.Result : default; } - private CommandResponse ProcessSequenceCommand(ICommandSequence sequenceCommand) + private CommandResponse ProcessSequenceCommand(ICommandSequence commandSequence) { CommandResponse lastStepResponse = null; - foreach (var command in sequenceCommand.Commands) + foreach (var command in commandSequence.Commands) { var response = Process(command, () => GetSequenceCommandResponse(command, lastStepResponse)); lastStepResponse = response; diff --git a/src/Kledex/Commands/ICommandSender.cs b/src/Kledex/Commands/ICommandSender.cs index 3dac8fe3..685f9238 100644 --- a/src/Kledex/Commands/ICommandSender.cs +++ b/src/Kledex/Commands/ICommandSender.cs @@ -16,11 +16,11 @@ public interface ICommandSender Task SendAsync(ICommand command); /// - /// Sends the specified sequence command asynchronously. + /// Sends the specified command sequence asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// - /// The sequence command. - Task SendAsync(ICommandSequence sequenceCommand); + /// The command sequence. + Task SendAsync(ICommandSequence commandSequence); /// /// Sends the specified command asynchronously. @@ -31,13 +31,13 @@ public interface ICommandSender Task SendAsync(ICommand command); /// - /// Sends the specified sequence command asynchronously. + /// Sends the specified command sequence asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The type of the result. - /// The sequence command. + /// The command sequence. /// A custom object set as result in the command hadler response. - Task SendAsync(ICommandSequence sequenceCommand); + Task SendAsync(ICommandSequence commandSequence); /// /// Sends the specified command. @@ -47,11 +47,11 @@ public interface ICommandSender void Send(ICommand command); /// - /// Sends the specified sequence command. + /// Sends the specified command sequence. /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. /// - /// The sequence command. - void Send(ICommandSequence sequenceCommand); + /// The command sequence. + void Send(ICommandSequence commandSequence); /// /// Sends the specified command. @@ -62,12 +62,12 @@ public interface ICommandSender TResult Send(ICommand command); /// - /// Sends the sequence specified sequence command. + /// Sends the sequence specified command sequence. /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. /// /// The type of the result. - /// The sequence command. + /// The command sequence. /// A custom object set as result in the command hadler response. - TResult Send(ICommandSequence sequenceCommand); + TResult Send(ICommandSequence commandSequence); } } diff --git a/src/Kledex/Dispatcher.cs b/src/Kledex/Dispatcher.cs index b484607f..99e5a263 100644 --- a/src/Kledex/Dispatcher.cs +++ b/src/Kledex/Dispatcher.cs @@ -37,9 +37,9 @@ public Task SendAsync(ICommand command) } /// - public Task SendAsync(ICommandSequence sequenceCommand) + public Task SendAsync(ICommandSequence commandSequence) { - return _commandSender.SendAsync(sequenceCommand); + return _commandSender.SendAsync(commandSequence); } /// @@ -49,9 +49,9 @@ public Task SendAsync(ICommand command) } /// - public Task SendAsync(ICommandSequence sequenceCommand) + public Task SendAsync(ICommandSequence commandSequence) { - return _commandSender.SendAsync(sequenceCommand); + return _commandSender.SendAsync(commandSequence); } /// @@ -81,9 +81,9 @@ public void Send(ICommand command) } /// - public void Send(ICommandSequence sequenceCommand) + public void Send(ICommandSequence commandSequence) { - _commandSender.Send(sequenceCommand); + _commandSender.Send(commandSequence); } /// @@ -93,9 +93,9 @@ public TResult Send(ICommand command) } /// - public TResult Send(ICommandSequence sequenceCommand) + public TResult Send(ICommandSequence commandSequence) { - return _commandSender.Send(sequenceCommand); + return _commandSender.Send(commandSequence); } /// diff --git a/src/Kledex/IDispatcher.cs b/src/Kledex/IDispatcher.cs index 179ea0ef..f6763922 100644 --- a/src/Kledex/IDispatcher.cs +++ b/src/Kledex/IDispatcher.cs @@ -19,11 +19,11 @@ public interface IDispatcher Task SendAsync(ICommand command); /// - /// Sends the specified sequence command asynchronously. + /// Sends the specified command sequence asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// - /// The sequence command. - Task SendAsync(ICommandSequence sequenceCommand); + /// The command sequence. + Task SendAsync(ICommandSequence commandSequence); /// /// Sends the specified command asynchronously. @@ -34,13 +34,13 @@ public interface IDispatcher Task SendAsync(ICommand command); /// - /// Sends the specified sequence command asynchronously. + /// Sends the specified command sequence asynchronously. /// The command handler must implement Kledex.Commands.ISequenceCommandHandlerAsync<TCommand>. /// /// The type of the result. - /// The sequence command. + /// The command sequence. /// A custom object set as result in the command hadler response. - Task SendAsync(ICommandSequence sequenceCommand); + Task SendAsync(ICommandSequence commandSequence); /// /// Asynchronously publishes the specified event. @@ -77,11 +77,11 @@ Task DispatchBusMessageAsync(TMessage message) void Send(ICommand command); /// - /// Sends the specified sequence command. + /// Sends the specified command sequence. /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. /// - /// The sequence command. - void Send(ICommandSequence sequenceCommand); + /// The command sequence. + void Send(ICommandSequence commandSequence); /// /// Sends the specified command. @@ -92,13 +92,13 @@ Task DispatchBusMessageAsync(TMessage message) TResult Send(ICommand command); /// - /// Sends the sequence specified sequence command. + /// Sends the sequence specified command sequence. /// The command handler must implement Kledex.Commands.ISequenceCommandHandler<TCommand>. /// /// The type of the result. - /// The sequence command. + /// The command sequence. /// A custom object set as result in the command hadler response. - TResult Send(ICommandSequence sequenceCommand); + TResult Send(ICommandSequence commandSequence); /// /// Publishes the specified event. From b8fa8bcc1df996f125afc120de9755628fb42424 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 22:31:39 +0000 Subject: [PATCH 16/22] Update Caching-Queries.md --- docs/Caching-Queries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Caching-Queries.md b/docs/Caching-Queries.md index 5cedc101..962368b7 100644 --- a/docs/Caching-Queries.md +++ b/docs/Caching-Queries.md @@ -1,6 +1,6 @@ -# Caching +# Caching Queries -With Kledex it is possible to automatically cache the result of a query using one of the available providers (Memory or Redis). First, you need to configure a cache provider as explained [here](Configuration#caching). Next, as per any normal queries, create the model for the result and a query. The only difference with a normal query is that it needs to inherit from the **CacheableQuery<>** abstract class (or implements the **ICacheableQuery<>** interface) in order to set the _Cache Key_ and the _Cache Time_: +With Kledex it is possible to automatically cache the result of a query using one of the available providers (Memory or Redis). First, you need to configure a cache provider as explained [here](Configuration#caching). Next, as per any normal queries, create the model for the result and the query. The only difference with a normal query is that it needs to inherit from the **CacheableQuery<>** abstract class (or implements the **ICacheableQuery<>** interface) in order to set the _Cache Key_ and the _Cache Time_: ```C# public class Something From 08420cfeb459cef5a4cd10741aabea15ec1c020c Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Thu, 21 Nov 2019 22:35:47 +0000 Subject: [PATCH 17/22] Update Command-Validation.md --- docs/Command-Validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Command-Validation.md b/docs/Command-Validation.md index 2f51b603..d8439750 100644 --- a/docs/Command-Validation.md +++ b/docs/Command-Validation.md @@ -16,7 +16,7 @@ public class CreateProduct : DomainCommand } ``` -The only validation provider currenlty available is the _FluentValidation_ provider. Thereofore, you need to create a class that inherits from the **AbstractValidator<>** class of _FluentValidation_: +The only validation provider currenlty available is the _FluentValidation_ provider. Therefore, you need to create a class that inherits from the **AbstractValidator<>** class of _FluentValidation_: ```C# public CreateProductValidator() @@ -34,7 +34,7 @@ public CreateProductValidator() } ``` -Kledex will automatically resolve and execute it. +Kledex will automatically resolve and execute it. To know more about FluentValidation please visit their [website](https://fluentvalidation.net). Note that the assemblies that contain the validators need to be registered the same way command, query and event handlers are as explained [here](Configuration#main). From 57ca7762b5ba0d976c4639bce86780c438230686 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Fri, 22 Nov 2019 09:49:13 +0000 Subject: [PATCH 18/22] Update Command-Sequence.md --- docs/Command-Sequence.md | 112 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/docs/Command-Sequence.md b/docs/Command-Sequence.md index 568dbf38..32adf165 100644 --- a/docs/Command-Sequence.md +++ b/docs/Command-Sequence.md @@ -1,6 +1,116 @@ # Command Sequence -Work in progress... +With Kledex is possible to create a squence of commands that will be executed in the specified order. + +First, create the commands that need to be part of the sequence: + +```C# +public class FirstCommand : Command +{ +} + +public class SecondCommand : Command +{ +} + +public class ThirdCommand : Command +{ +} +``` + +Next, as you would have done normally create the handlers for your commands. +The only difference is that the handlers need to implement the **ISequenceCommandHandlerAsync<>** interface. +The _HandlerAsync_ of this interface accepts an extra parameter which is a _CommandResponse_. +Kledex will pass automatically the command response of the previous command in the sequence. +For the first hanlder it would obviously be null. + +First command handler: + +```C# +public class FirstCommandHandler : ISequenceCommandHandlerAsync +{ + public Task HandleAsync(FirstCommand command, CommandResponse previousStepResponse) + { + Console.WriteLine("Message from first command handler"); + + return Task.FromResult(new CommandResponse + { + Result = "First result" + }); + } +} +``` + +Second command handler: + +```C# +public class SecondCommandHandler : ISequenceCommandHandlerAsync +{ + public Task HandleAsync(SecondCommand command, CommandResponse previousStepResponse) + { + Console.WriteLine($"Message from second command handler. Result from first handler: {previousStepResponse.Result}"); + + return Task.FromResult(new CommandResponse + { + Result = "Second result" + }); + } +} +``` + +Third command handler: + +```C# +public class ThirdCommandHandler : ISequenceCommandHandlerAsync +{ + public Task HandleAsync(ThirdCommand command, CommandResponse previousStepResponse) + { + Console.WriteLine($"Message from third command handler. Result from second handler: {previousStepResponse.Result}"); + + return Task.FromResult(new CommandResponse + { + Result = "Third result" + }); + } +} +``` + +Last, create a class that inherits from the _CommandSequence_ abstract class and add all yoyr commands: + +```C# +public class SampleCommandSequence : CommandSequence +{ + public SampleCommandSequence() + { + AddCommand(new FirstCommand()); + AddCommand(new SecondCommand()); + AddCommand(new ThirdCommand()); + } +} +``` + +Use the dispatcher to execute the command sequence: + +```C# +await dispatcher.SendAsync(new SampleCommandSequence()); +``` + +You can also get a result from the sequence which will be the value of the Result property of the command response of the last command in the sequence (in our example of the ThirdCommand): + +```C# +var result = await dispatcher.SendAsync(new SampleCommandSequence()); +``` + +The following is the output generated: + +``` +Message from first command handler +Message from second command handler. Result from first handler: First result +Message from third command handler. Result from second handler: Second result +Final result: Third result +``` + +You can find the sample code [here](https://github.com/lucabriguglia/Kledex/tree/master/samples/Kledex.Sample.CommandSequence) ## Related From 48d8b742816a99c96fd619e9c57a0d00845ada67 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Fri, 22 Nov 2019 09:51:08 +0000 Subject: [PATCH 19/22] Update Command-Sequence.md --- docs/Command-Sequence.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Command-Sequence.md b/docs/Command-Sequence.md index 32adf165..717c6c9f 100644 --- a/docs/Command-Sequence.md +++ b/docs/Command-Sequence.md @@ -75,7 +75,7 @@ public class ThirdCommandHandler : ISequenceCommandHandlerAsync } ``` -Last, create a class that inherits from the _CommandSequence_ abstract class and add all yoyr commands: +Last, create a class that inherits from the _CommandSequence_ abstract class and add all your commands: ```C# public class SampleCommandSequence : CommandSequence From 8b73d9303ad91bfbe85e55d85987d000a6d6ddbf Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Fri, 22 Nov 2019 10:36:10 +0000 Subject: [PATCH 20/22] Update UI.md --- docs/UI.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/UI.md b/docs/UI.md index 2f77de72..230e0027 100644 --- a/docs/UI.md +++ b/docs/UI.md @@ -1,3 +1,15 @@ # UI -Work in progress... +The UI package is an experimental package that can be used to display information about an aggregate and its events: + +```C# +var model = await _dispatcher.GetResultAsync(new GetAggregateModel +{ + AggregateRootId = id +}); +``` + +The model will contain the list of all events of the aggregate order by time stamp descending. +Further improvements will be made in coming versions. + +You can find a sample usage [here](https://github.com/lucabriguglia/Kledex/blob/master/samples/Kledex.Sample.EventSourcing/Pages/Edit.cshtml.cs) From 0c657491478410a6959e8962eaffcdc200fb2397 Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Fri, 22 Nov 2019 10:37:00 +0000 Subject: [PATCH 21/22] Update UI.md --- docs/UI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UI.md b/docs/UI.md index 230e0027..b462a3ef 100644 --- a/docs/UI.md +++ b/docs/UI.md @@ -9,7 +9,7 @@ var model = await _dispatcher.GetResultAsync(new GetAggregateModel }); ``` -The model will contain the list of all events of the aggregate order by time stamp descending. +The model will contain the list of all events of the aggregate ordered by time stamp descending. Further improvements will be made in coming versions. You can find a sample usage [here](https://github.com/lucabriguglia/Kledex/blob/master/samples/Kledex.Sample.EventSourcing/Pages/Edit.cshtml.cs) From ef6dffe90e73f9974d73b8f55e2d2582b2384acd Mon Sep 17 00:00:00 2001 From: Luca Briguglia Date: Fri, 22 Nov 2019 10:37:15 +0000 Subject: [PATCH 22/22] Update UI.md --- docs/UI.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/UI.md b/docs/UI.md index b462a3ef..0f3d7984 100644 --- a/docs/UI.md +++ b/docs/UI.md @@ -12,4 +12,4 @@ var model = await _dispatcher.GetResultAsync(new GetAggregateModel The model will contain the list of all events of the aggregate ordered by time stamp descending. Further improvements will be made in coming versions. -You can find a sample usage [here](https://github.com/lucabriguglia/Kledex/blob/master/samples/Kledex.Sample.EventSourcing/Pages/Edit.cshtml.cs) +You can find a sample usage [here](https://github.com/lucabriguglia/Kledex/blob/master/samples/Kledex.Sample.EventSourcing/Pages/Edit.cshtml.cs).