diff --git a/.all-contributorsrc b/.all-contributorsrc
index e5642eea..c002b2f5 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -9,7 +9,7 @@
"login": "KyleMcMaster",
"name": "Kyle McMaster",
"avatar_url": "https://avatars1.githubusercontent.com/u/11415127?v=4",
- "profile": "https://github.com/KyleMcMaster",
+ "profile": "https://www.KyleMcMaster.com",
"contributions": [
"design",
"code",
diff --git a/.editorconfig b/.editorconfig
index ab4fa5dd..b82269d8 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -112,7 +112,7 @@ csharp_style_unused_value_assignment_preference = discard_variable:suggestion
csharp_style_unused_value_expression_statement_preference = discard_variable:silent
# 'using' directive preferences
-csharp_using_directive_placement = outside_namespace:silent
+csharp_using_directive_placement = outside_namespace:suggestion
#### C# Formatting Rules ####
diff --git a/README.md b/README.md
index 6d9d0a51..5b7a2fbf 100644
--- a/README.md
+++ b/README.md
@@ -25,11 +25,11 @@ Sample HRIS application where a list of employees and their payroll information
### Api and Functions
-
+
### Client
-
+
[](https://prettier.io)
## Motivation
diff --git a/api/Directory.Build.props b/api/Directory.Build.props
index f49bdde1..0ac63c67 100644
--- a/api/Directory.Build.props
+++ b/api/Directory.Build.props
@@ -2,6 +2,7 @@
latest
enable
- CS8600;CS8602;CS8603
+ true
+ true
diff --git a/api/Directory.Packages.props b/api/Directory.Packages.props
index 90752eae..778b0c2c 100644
--- a/api/Directory.Packages.props
+++ b/api/Directory.Packages.props
@@ -3,44 +3,45 @@
true
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
+
+
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
\ No newline at end of file
diff --git a/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj b/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj
index 642932e6..e04591d8 100644
--- a/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj
+++ b/api/PayrollProcessor.Core.Domain.Tests/PayrollProcessor.Core.Domain.Tests.csproj
@@ -11,9 +11,15 @@
-
-
-
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs
new file mode 100644
index 00000000..00faf9be
--- /dev/null
+++ b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandDispatcher.cs
@@ -0,0 +1,10 @@
+using System.Threading;
+using CSharpFunctionalExtensions;
+
+namespace PayrollProcessor.Core.Domain.Intrastructure.Operations.Commands;
+public interface IStranglerCommandDispatcher
+{
+ Result Dispatch(ICommand command, CancellationToken token = default);
+
+ Result Dispatch(ICommand command, CancellationToken token = default);
+}
diff --git a/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs
new file mode 100644
index 00000000..94446a62
--- /dev/null
+++ b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/IStranglerCommandHandler.cs
@@ -0,0 +1,13 @@
+using System.Threading;
+using CSharpFunctionalExtensions;
+
+namespace PayrollProcessor.Core.Domain.Intrastructure.Operations.Commands;
+public interface IStranglerCommandHandler where TCommand : ICommand
+{
+ Result Execute(TCommand command, CancellationToken token);
+}
+
+public interface IStranglerCommandHandler where TCommand : ICommand
+{
+ Result Execute(TCommand command, CancellationToken token);
+}
diff --git a/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs
new file mode 100644
index 00000000..3159c62a
--- /dev/null
+++ b/api/PayrollProcessor.Core.Domain/Intrastructure/Operations/Commands/StranglerCommandDispatcher.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Concurrent;
+using System.Threading;
+using Ardalis.GuardClauses;
+using CSharpFunctionalExtensions;
+using PayrollProcessor.Core.Domain.Intrastructure.Operations.Factories;
+
+namespace PayrollProcessor.Core.Domain.Intrastructure.Operations.Commands;
+
+///
+/// TODO: Temporarily named after the Strangler Fig Pattern as this serves as an implementation of migrating to CSharpFunctionalExtensions from LanguageExt.
+///
+public class StranglerCommandDispatcher : IStranglerCommandDispatcher
+{
+ private readonly ServiceProviderDelegate serviceProvider;
+ private static readonly ConcurrentDictionary commandHandlers = new ConcurrentDictionary();
+
+ public StranglerCommandDispatcher(ServiceProviderDelegate serviceProvider)
+ {
+ Guard.Against.Null(serviceProvider, nameof(serviceProvider));
+
+ this.serviceProvider = serviceProvider;
+ }
+
+ public Result Dispatch(ICommand command, CancellationToken token = default)
+ {
+ Guard.Against.Null(command, nameof(command));
+
+ var commandType = command.GetType();
+
+ var handler = (StranglerCommandHandlerWrapper)commandHandlers
+ .GetOrAdd(
+ commandType,
+#pragma warning disable CS8603 // Possible null reference return.
+ t => Activator
+ .CreateInstance(typeof(StranglerCommandHandlerWrapperImpl<>)
+ .MakeGenericType(commandType)));
+#pragma warning restore CS8603 // Possible null reference return.
+
+ return handler.Dispatch(command, serviceProvider, token);
+ }
+
+ public Result Dispatch(ICommand command, CancellationToken token = default)
+ {
+ Guard.Against.Null(command, nameof(command));
+
+ var commandType = command.GetType();
+
+ var handler = (StranglerCommandHandlerWrapper)commandHandlers
+ .GetOrAdd(
+ commandType,
+#pragma warning disable CS8603 // Possible null reference return.
+ t => Activator
+ .CreateInstance(typeof(StranglerCreateCommandHandlerWrapperImpl<,>)
+ .MakeGenericType(commandType, typeof(TResponse))));
+#pragma warning restore CS8603 // Possible null reference return.
+
+ return handler.Dispatch(command, serviceProvider, token);
+ }
+}
+
+internal abstract class StranglerCommandHandlerWrapper : HandlerBase
+{
+ public abstract Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token);
+}
+
+internal class StranglerCommandHandlerWrapperImpl : StranglerCommandHandlerWrapper
+ where TCommand : ICommand
+{
+ public override Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token) =>
+ GetHandler>(serviceProvider).Execute((TCommand)command, token);
+}
+
+internal abstract class StranglerCommandHandlerWrapper : HandlerBase
+{
+ public abstract Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token);
+}
+
+internal class StranglerCreateCommandHandlerWrapperImpl : StranglerCommandHandlerWrapper
+ where TCommand : ICommand
+{
+ public override Result Dispatch(ICommand command, ServiceProviderDelegate serviceProvider, CancellationToken token) =>
+ GetHandler>(serviceProvider).Execute((TCommand)command, token);
+}
diff --git a/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj b/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj
index 1f12a38d..1ccb2845 100644
--- a/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj
+++ b/api/PayrollProcessor.Core.Domain/PayrollProcessor.Core.Domain.csproj
@@ -6,6 +6,7 @@
+
diff --git a/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj b/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj
index 5f5c7758..b10b13fa 100644
--- a/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj
+++ b/api/PayrollProcessor.Data.Persistence.Tests/PayrollProcessor.Data.Persistence.Tests.csproj
@@ -11,8 +11,14 @@
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj b/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj
index 59c55c77..0e6b1342 100644
--- a/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj
+++ b/api/PayrollProcessor.Functions.Api.Tests/PayrollProcessor.Functions.Api.Tests.csproj
@@ -13,8 +13,14 @@
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj b/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj
index 4ca5c752..cf313408 100644
--- a/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj
+++ b/api/PayrollProcessor.Web.Api.Tests/PayrollProcessor.Web.Api.Tests.csproj
@@ -10,8 +10,14 @@
-
-
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
diff --git a/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs b/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs
index a199384d..447083c9 100644
--- a/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs
+++ b/api/PayrollProcessor.Web.Api/Features/Employees/EmployeeCreate.cs
@@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Ardalis.ApiEndpoints;
using Ardalis.GuardClauses;
+using CSharpFunctionalExtensions;
using Microsoft.AspNetCore.Mvc;
using PayrollProcessor.Core.Domain.Features.Employees;
using PayrollProcessor.Core.Domain.Intrastructure.Identifiers;
@@ -11,14 +12,14 @@
namespace PayrollProcessor.Web.Api.Features.Employees;
-public class EmployeeCreate : EndpointBaseAsync
+public class EmployeeCreate : EndpointBaseSync
.WithRequest
.WithActionResult
{
- private readonly ICommandDispatcher dispatcher;
+ private readonly IStranglerCommandDispatcher dispatcher;
private readonly IEntityIdGenerator generator;
- public EmployeeCreate(ICommandDispatcher dispatcher, IEntityIdGenerator generator)
+ public EmployeeCreate(IStranglerCommandDispatcher dispatcher, IEntityIdGenerator generator)
{
Guard.Against.Null(dispatcher, nameof(dispatcher));
Guard.Against.Null(generator, nameof(generator));
@@ -34,7 +35,7 @@ public EmployeeCreate(ICommandDispatcher dispatcher, IEntityIdGenerator generato
OperationId = "Employees.Create",
Tags = new[] { "Employees" })
]
- public override Task> HandleAsync([FromBody] EmployeeCreateRequest request, CancellationToken token)
+ public override ActionResult Handle([FromBody] EmployeeCreateRequest request)
{
var command = new EmployeeCreateCommand(
generator.Generate(),
@@ -52,9 +53,9 @@ public override Task> HandleAsync([FromBody] EmployeeCrea
return dispatcher
.Dispatch(command)
- .Match>(
- employee => employee,
- ex => new APIErrorResult(ex.Message));
+ .Match, Employee>(
+ onSuccess: employee => Ok(employee),
+ onFailure: ex => new APIErrorResult(ex));
}
}
diff --git a/payroll-processor.code-workspace b/payroll-processor.code-workspace
index 1377e2b2..fa9ab3fe 100644
--- a/payroll-processor.code-workspace
+++ b/payroll-processor.code-workspace
@@ -1,5 +1,9 @@
{
"folders": [
+ {
+ "path": ".",
+ "name": "root"
+ },
{
"path": "docs",
"name": "docs"
@@ -15,10 +19,6 @@
{
"path": "vue-client",
"name": "vue"
- },
- {
- "path": ".",
- "name": "root"
}
],
"settings": {
@@ -32,11 +32,11 @@
"api": true,
"client": true,
"docs": true,
+ "vue-client": true,
"**/bin": true,
"**/obj": true,
"**/dist": true
},
-
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
@@ -52,12 +52,11 @@
*/
"editor.codeActionsOnSave": {}
},
-
- "omnisharp.defaultLaunchSolution": "PayrollProcessor.sln",
"omnisharp.autoStart": true,
"omnisharp.enableEditorConfigSupport": true,
"omnisharp.enableRoslynAnalyzers": true,
- "omnisharp.useEditorFormattingSettings": true
+ "omnisharp.useEditorFormattingSettings": true,
+ "dotnet.defaultSolution": "PayrollProcessor.sln"
},
"extensions": {
"recommendations": [