From b953380822a789a1cbede24cfc09579fd03ffa90 Mon Sep 17 00:00:00 2001
From: Tomohisa Takaoka <ttakaoka@jtechs.com>
Date: Tue, 15 Aug 2023 12:28:47 -0700
Subject: [PATCH] first working sample

---
 Sekiban.sln                                   | 14 +++
 fsCustomerTest/Program.fs                     |  1 +
 fsCustomerTest/Tests.fs                       | 47 ++++++++++
 fsCustomerTest/fsCustomerTest.fsproj          | 34 +++++++
 global.json                                   |  7 ++
 .../Shared/FeatureCheckDependency.cs          |  2 +-
 .../Mixed.Domain/MixedContextDependency.cs    |  2 +-
 .../Aggregates/MultiTenantDependency.cs       |  2 +-
 samples/Shipping.Domain/ShippingDependency.cs |  2 +-
 .../Warehouse.Domain/WarehouseDependency.cs   |  2 +-
 samples/fsharp/Dependency.fs                  | 24 +++++
 samples/fsharp/Domain.fs                      | 92 +++++++++++++++++++
 samples/fsharp/Library.fs                     |  5 +
 samples/fsharp/fsCustomer.fsproj              | 24 +++++
 src/Sekiban.Core/Command/CommandExecutor.cs   |  4 +-
 .../Command/CommandHandlerAdapter.cs          |  2 +-
 src/Sekiban.Core/Command/ICommandHandler.cs   |  2 +-
 .../IVersionValidationCommandHandler.cs       |  2 +-
 .../AggregateDependencyDefinition.cs          |  8 +-
 .../DomainDependencyDefinitionBase.cs         |  9 +-
 .../Dependency/IDependencyDefinition.cs       |  1 +
 .../SekibanEventSourcingDependency.cs         |  7 +-
 .../Events/RegisteredEventTypes.cs            |  3 +-
 .../EmptyDependencyDefinition.cs              |  2 +-
 24 files changed, 274 insertions(+), 24 deletions(-)
 create mode 100644 fsCustomerTest/Program.fs
 create mode 100644 fsCustomerTest/Tests.fs
 create mode 100644 fsCustomerTest/fsCustomerTest.fsproj
 create mode 100644 global.json
 create mode 100644 samples/fsharp/Dependency.fs
 create mode 100644 samples/fsharp/Domain.fs
 create mode 100644 samples/fsharp/Library.fs
 create mode 100644 samples/fsharp/fsCustomer.fsproj

diff --git a/Sekiban.sln b/Sekiban.sln
index 2b50bada..c48210a0 100644
--- a/Sekiban.sln
+++ b/Sekiban.sln
@@ -57,6 +57,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Convert011To012", "tools\Co
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixed.Test", "test\Mixed.Test\Mixed.Test.csproj", "{630F88BB-1967-4F9A-B6E2-771922069319}"
 EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "fsCustomer", "samples\fsharp\fsCustomer.fsproj", "{3BDC7B2C-F56C-4EDF-AE50-D2ADCF9538A3}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "fsCustomerTest", "fsCustomerTest\fsCustomerTest.fsproj", "{8E69545E-24E1-48D2-A0C5-5219FF2E36D9}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -155,6 +159,14 @@ Global
 		{630F88BB-1967-4F9A-B6E2-771922069319}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{630F88BB-1967-4F9A-B6E2-771922069319}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{630F88BB-1967-4F9A-B6E2-771922069319}.Release|Any CPU.Build.0 = Release|Any CPU
+		{3BDC7B2C-F56C-4EDF-AE50-D2ADCF9538A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{3BDC7B2C-F56C-4EDF-AE50-D2ADCF9538A3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{3BDC7B2C-F56C-4EDF-AE50-D2ADCF9538A3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{3BDC7B2C-F56C-4EDF-AE50-D2ADCF9538A3}.Release|Any CPU.Build.0 = Release|Any CPU
+		{8E69545E-24E1-48D2-A0C5-5219FF2E36D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8E69545E-24E1-48D2-A0C5-5219FF2E36D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8E69545E-24E1-48D2-A0C5-5219FF2E36D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8E69545E-24E1-48D2-A0C5-5219FF2E36D9}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
@@ -183,6 +195,8 @@ Global
 		{091121AC-BDAB-46B3-8A49-A63D0EF6FAA5} = {B85F80AE-6A27-402C-93FA-352D938EDA8D}
 		{482C14AE-C213-4234-9B11-F0D7E953EEE2} = {9504B5F4-E803-46E3-B110-CBA8196B03D4}
 		{630F88BB-1967-4F9A-B6E2-771922069319} = {4C34F748-F223-44DD-899D-029F9CEF7780}
+		{3BDC7B2C-F56C-4EDF-AE50-D2ADCF9538A3} = {B85F80AE-6A27-402C-93FA-352D938EDA8D}
+		{8E69545E-24E1-48D2-A0C5-5219FF2E36D9} = {4C34F748-F223-44DD-899D-029F9CEF7780}
 	EndGlobalSection
 	GlobalSection(ExtensibilityGlobals) = postSolution
 		SolutionGuid = {2DFBE53E-69A7-453A-8D20-72271AE32016}
diff --git a/fsCustomerTest/Program.fs b/fsCustomerTest/Program.fs
new file mode 100644
index 00000000..0695f84c
--- /dev/null
+++ b/fsCustomerTest/Program.fs
@@ -0,0 +1 @@
+module Program = let [<EntryPoint>] main _ = 0
diff --git a/fsCustomerTest/Tests.fs b/fsCustomerTest/Tests.fs
new file mode 100644
index 00000000..30f69ff5
--- /dev/null
+++ b/fsCustomerTest/Tests.fs
@@ -0,0 +1,47 @@
+module fsCustomerTest.Tests
+
+open System.Text.Json
+open Sekiban.Core.Shared
+open Sekiban.Testing.SingleProjections
+open Xunit
+open Xunit.Abstractions
+open fsCustomer.Dependency
+open fsCustomer.Domain
+
+[<Fact>]
+let ``My test`` () = Assert.True(true)
+
+
+
+
+type BranchSpec(testOutputHelper: ITestOutputHelper) =
+    inherit AggregateTest<Branch, FsCustomerDependency>()
+    member this.TestOutputHelper = testOutputHelper
+
+    [<Fact>]
+    member this.Serialize() =
+        let serialized = SekibanJsonHelper.Serialize(Branch("Japan"))
+        this.TestOutputHelper.WriteLine(serialized)
+        let deserialized = SekibanJsonHelper.Deserialize<Branch>(serialized)
+        let serializedFromDeserialized = SekibanJsonHelper.Serialize(deserialized)
+        this.TestOutputHelper.WriteLine(serializedFromDeserialized)
+        Assert.Equal(serialized, serializedFromDeserialized)
+
+    [<Fact>]
+    member this.SerializeOptionChecking() =
+        let options: JsonSerializerOptions = JsonSerializerOptions()
+        //      options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
+        //        options.PropertyNameCaseInsensitive = true
+        let serialized = JsonSerializer.Serialize<Branch>(Branch("Japan"), options)
+        this.TestOutputHelper.WriteLine(serialized)
+        let deserialized = JsonSerializer.Deserialize<Branch>(serialized, options)
+
+        let serializedFromDeserialized =
+            JsonSerializer.Serialize<Branch>(deserialized, options)
+
+        this.TestOutputHelper.WriteLine(serializedFromDeserialized)
+        Assert.Equal(serialized, serializedFromDeserialized)
+
+    [<Fact>]
+    member this.CreateAggregate() =
+        this.WhenCommand(CreateBranch("Japan")).ThenPayloadIs(Branch("Japan"))
diff --git a/fsCustomerTest/fsCustomerTest.fsproj b/fsCustomerTest/fsCustomerTest.fsproj
new file mode 100644
index 00000000..8eab425b
--- /dev/null
+++ b/fsCustomerTest/fsCustomerTest.fsproj
@@ -0,0 +1,34 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+
+        <IsPackable>false</IsPackable>
+        <GenerateProgramFile>false</GenerateProgramFile>
+        <IsTestProject>true</IsTestProject>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <Compile Include="Tests.fs"/>
+        <Compile Include="Program.fs"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0"/>
+        <PackageReference Include="xunit" Version="2.5.0"/>
+        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+            <PrivateAssets>all</PrivateAssets>
+        </PackageReference>
+        <PackageReference Include="coverlet.collector" Version="3.2.0">
+            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+            <PrivateAssets>all</PrivateAssets>
+        </PackageReference>
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\samples\fsharp\fsCustomer.fsproj"/>
+        <ProjectReference Include="..\src\Sekiban.Testing\Sekiban.Testing.csproj"/>
+    </ItemGroup>
+
+</Project>
diff --git a/global.json b/global.json
new file mode 100644
index 00000000..dad2db5e
--- /dev/null
+++ b/global.json
@@ -0,0 +1,7 @@
+{
+  "sdk": {
+    "version": "8.0.0",
+    "rollForward": "latestMajor",
+    "allowPrerelease": true
+  }
+}
\ No newline at end of file
diff --git a/samples/FeatureCheck.Domain/Shared/FeatureCheckDependency.cs b/samples/FeatureCheck.Domain/Shared/FeatureCheckDependency.cs
index ae58ae25..45d9a14d 100644
--- a/samples/FeatureCheck.Domain/Shared/FeatureCheckDependency.cs
+++ b/samples/FeatureCheck.Domain/Shared/FeatureCheckDependency.cs
@@ -47,7 +47,7 @@ public class FeatureCheckDependency : DomainDependencyDefinitionBase
 {
     public override Assembly GetExecutingAssembly() => Assembly.GetExecutingAssembly();
 
-    protected override void Define()
+    public override void Define()
     {
         AddAggregate<Branch>()
             .AddCommandHandler<CreateBranch, CreateBranch.Handler>()
diff --git a/samples/Mixed.Domain/MixedContextDependency.cs b/samples/Mixed.Domain/MixedContextDependency.cs
index b52503c2..118cffbc 100644
--- a/samples/Mixed.Domain/MixedContextDependency.cs
+++ b/samples/Mixed.Domain/MixedContextDependency.cs
@@ -8,7 +8,7 @@ public class MixedContextDependency : DomainDependencyDefinitionBase
 {
     public override Assembly GetExecutingAssembly() => Assembly.GetExecutingAssembly();
 
-    protected override void Define()
+    public override void Define()
     {
         AddDependency<ShippingDependency>()
             .AddDependency<WarehouseDependency>();
diff --git a/samples/MultiTenant.Domain/Aggregates/MultiTenantDependency.cs b/samples/MultiTenant.Domain/Aggregates/MultiTenantDependency.cs
index 3e28a35f..c4e06402 100644
--- a/samples/MultiTenant.Domain/Aggregates/MultiTenantDependency.cs
+++ b/samples/MultiTenant.Domain/Aggregates/MultiTenantDependency.cs
@@ -9,7 +9,7 @@ public class MultiTenantDependency : DomainDependencyDefinitionBase
 {
 
     public override Assembly GetExecutingAssembly() => Assembly.GetExecutingAssembly();
-    protected override void Define()
+    public override void Define()
     {
         AddAggregate<ClientPayload>().AddCommandHandler<CreateClient, CreateClient.Handler>().AddAggregateListQuery<ClientListQuery>();
     }
diff --git a/samples/Shipping.Domain/ShippingDependency.cs b/samples/Shipping.Domain/ShippingDependency.cs
index a279d889..cbf86a43 100644
--- a/samples/Shipping.Domain/ShippingDependency.cs
+++ b/samples/Shipping.Domain/ShippingDependency.cs
@@ -9,7 +9,7 @@ public class ShippingDependency : DomainDependencyDefinitionBase
 {
     public override Assembly GetExecutingAssembly() => Assembly.GetExecutingAssembly();
 
-    protected override void Define()
+    public override void Define()
     {
         AddAggregate<Product>()
             .AddCommandHandler<CreateProduct, CreateProduct.Handler>()
diff --git a/samples/Warehouse.Domain/WarehouseDependency.cs b/samples/Warehouse.Domain/WarehouseDependency.cs
index c1760a74..97f5a3bf 100644
--- a/samples/Warehouse.Domain/WarehouseDependency.cs
+++ b/samples/Warehouse.Domain/WarehouseDependency.cs
@@ -8,7 +8,7 @@ public class WarehouseDependency : DomainDependencyDefinitionBase
 {
     public override Assembly GetExecutingAssembly() => Assembly.GetExecutingAssembly();
 
-    protected override void Define()
+    public override void Define()
     {
         AddAggregate<ProductStock>()
             .AddCommandHandler<AddProductStock, AddProductStock.Handler>()
diff --git a/samples/fsharp/Dependency.fs b/samples/fsharp/Dependency.fs
new file mode 100644
index 00000000..c27acb6f
--- /dev/null
+++ b/samples/fsharp/Dependency.fs
@@ -0,0 +1,24 @@
+module fsCustomer.Dependency
+
+open System.Reflection
+open Sekiban.Core.Dependency
+open fsCustomer.Domain
+
+
+type FsCustomerDependency() =
+    inherit DomainDependencyDefinitionBase()
+
+    override this.Define() =
+        do
+            this
+                .AddAggregate<Branch>()
+                .AddCommandHandler<CreateBranch, CreateBranchHandler>()
+            |> ignore
+
+            this
+                .AddAggregate<Client>()
+                .AddCommandHandler<CreateClient, CreateClientHandler>()
+                .AddAggregateQuery<ClientEmailExistsQuery>()
+            |> ignore
+
+    override this.GetExecutingAssembly() = Assembly.GetExecutingAssembly()
diff --git a/samples/fsharp/Domain.fs b/samples/fsharp/Domain.fs
new file mode 100644
index 00000000..d0816f06
--- /dev/null
+++ b/samples/fsharp/Domain.fs
@@ -0,0 +1,92 @@
+module fsCustomer.Domain
+
+open System
+open System.Text.Json.Serialization
+open FSharp.Control
+open Sekiban.Core.Aggregate
+open Sekiban.Core.Command
+open Sekiban.Core.Events
+open Sekiban.Core.Query.QueryModel
+
+type Branch [<JsonConstructor>] (name: string) =
+    member this.Name = name
+    interface IAggregatePayload
+    new() = Branch("")
+
+type CreateBranch(name: string) =
+    member this.Name = name
+
+    interface ICommand<Branch> with
+        member this.GetAggregateId() = Guid.NewGuid()
+
+type BranchCreated =
+    { Name: string }
+
+    interface IEventPayload<Branch, Branch, BranchCreated> with
+        static member OnEvent(aggregatePayload, ev) = Branch(ev.Payload.Name)
+
+type CreateBranchHandler() =
+    interface ICommandHandler<Branch, CreateBranch> with
+        member this.HandleCommandAsync(getAggregateState, command) =
+            taskSeq { yield { Name = command.Name } :> IEventPayloadApplicableTo<Branch> }
+
+
+type Client(name: string, email: string, branchId: Guid) =
+    member this.Name = name
+    member this.Email = email
+    member this.BranchId = branchId
+    interface IAggregatePayload
+    new() = Client("", "", Guid.Empty)
+
+type CreateClient =
+    { Name: string
+      Email: string
+      BranchId: Guid }
+
+    interface ICommand<Client> with
+        member this.GetAggregateId() = Guid.NewGuid()
+
+type ClientCreated =
+    { Name: string
+      Email: string
+      BranchId: Guid }
+
+    interface IEventPayload<Client, Client, ClientCreated> with
+        static member OnEvent(aggregatePayload, ev) =
+            Client(ev.Payload.Name, ev.Payload.Email, ev.Payload.BranchId)
+
+type ClientEmailExistsQueryResponse =
+    { Exists: bool }
+
+    interface IQueryResponse
+
+type ClientEmailExistsQueryParam =
+    { Email: string }
+
+    interface IQueryParameter<ClientEmailExistsQueryResponse>
+
+
+
+type ClientEmailExistsQuery =
+    interface IAggregateQuery<Client, ClientEmailExistsQueryParam, ClientEmailExistsQueryResponse> with
+        member this.HandleFilter(queryParam, list) =
+            { Exists = list |> Seq.exists (fun client -> client.Payload.Email = queryParam.Email) }
+
+type CreateClientHandler(queryExecutor: IQueryExecutor) =
+    member this.QueryExecutor = queryExecutor
+
+    interface ICommandHandler<Client, CreateClient> with
+        member this.HandleCommandAsync(getAggregateState, command) =
+            taskSeq {
+                let branchExistsResponse =
+                    this.QueryExecutor.ExecuteAsync({ Email = command.Email })
+                    |> Async.AwaitTask
+                    |> Async.RunSynchronously
+
+                if branchExistsResponse.Exists then
+                    yield
+                        { Name = command.Name
+                          Email = command.Email
+                          BranchId = command.BranchId }
+                        :> IEventPayloadApplicableTo<Client>
+            }
diff --git a/samples/fsharp/Library.fs b/samples/fsharp/Library.fs
new file mode 100644
index 00000000..8b1d2757
--- /dev/null
+++ b/samples/fsharp/Library.fs
@@ -0,0 +1,5 @@
+namespace fsCustomer
+
+module Say =
+    let hello name =
+        printfn "Hello %s" name
diff --git a/samples/fsharp/fsCustomer.fsproj b/samples/fsharp/fsCustomer.fsproj
new file mode 100644
index 00000000..c8cdf1d8
--- /dev/null
+++ b/samples/fsharp/fsCustomer.fsproj
@@ -0,0 +1,24 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+    <PropertyGroup>
+        <TargetFramework>net8.0</TargetFramework>
+        <GenerateDocumentationFile>true</GenerateDocumentationFile>
+        <LangVersion>default</LangVersion>
+    </PropertyGroup>
+
+    <ItemGroup>
+        <Compile Include="Library.fs"/>
+        <Compile Include="Domain.fs"/>
+        <Compile Include="Dependency.fs"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <ProjectReference Include="..\..\src\Sekiban.Core\Sekiban.Core.csproj"/>
+    </ItemGroup>
+
+    <ItemGroup>
+        <PackageReference Include="FSharp.Control.AsyncSeq" Version="3.2.1"/>
+        <PackageReference Include="FSharp.Control.TaskSeq" Version="0.3.0"/>
+    </ItemGroup>
+
+</Project>
diff --git a/src/Sekiban.Core/Command/CommandExecutor.cs b/src/Sekiban.Core/Command/CommandExecutor.cs
index 6e18691a..190fc887 100644
--- a/src/Sekiban.Core/Command/CommandExecutor.cs
+++ b/src/Sekiban.Core/Command/CommandExecutor.cs
@@ -90,7 +90,7 @@ public async Task<CommandExecutorResponseWithEvents> ExecCommandWithoutValidatio
 
     public async Task<(CommandExecutorResponse, List<IEvent>)> ExecCommandAsyncTyped<TAggregatePayload, TCommand>(
         TCommand command,
-        List<CallHistory>? callHistories = null) where TAggregatePayload : IAggregatePayloadCommon where TCommand : ICommand<TAggregatePayload>
+        List<CallHistory>? callHistories = null) where TAggregatePayload : IAggregatePayloadCommon, new() where TCommand : ICommand<TAggregatePayload>
     {
         var validationResult = command.ValidateProperties().ToList();
         if (validationResult.Any())
@@ -108,7 +108,7 @@ public async Task<CommandExecutorResponseWithEvents> ExecCommandWithoutValidatio
 
     public async Task<(CommandExecutorResponse, List<IEvent>)> ExecCommandWithoutValidationAsyncTyped<TAggregatePayload, TCommand>(
         TCommand command,
-        List<CallHistory>? callHistories = null) where TAggregatePayload : IAggregatePayloadCommon where TCommand : ICommand<TAggregatePayload>
+        List<CallHistory>? callHistories = null) where TAggregatePayload : IAggregatePayloadCommon, new() where TCommand : ICommand<TAggregatePayload>
     {
         var rootPartitionKey = command.GetRootPartitionKey();
         if (!CommandRootPartitionValidationAttribute.IsValidRootPartitionKey(rootPartitionKey))
diff --git a/src/Sekiban.Core/Command/CommandHandlerAdapter.cs b/src/Sekiban.Core/Command/CommandHandlerAdapter.cs
index 7cc2cca6..64367031 100644
--- a/src/Sekiban.Core/Command/CommandHandlerAdapter.cs
+++ b/src/Sekiban.Core/Command/CommandHandlerAdapter.cs
@@ -9,7 +9,7 @@ namespace Sekiban.Core.Command;
 ///     System use command handler adapter.
 ///     Application Developer does not need to implement this interface
 /// </summary>
-public sealed class CommandHandlerAdapter<TAggregatePayload, TCommand> where TAggregatePayload : IAggregatePayloadCommon
+public sealed class CommandHandlerAdapter<TAggregatePayload, TCommand> where TAggregatePayload : IAggregatePayloadCommon, new()
     where TCommand : ICommand<TAggregatePayload>
 {
     private readonly IAggregateLoader _aggregateLoader;
diff --git a/src/Sekiban.Core/Command/ICommandHandler.cs b/src/Sekiban.Core/Command/ICommandHandler.cs
index 0b6c772e..baae1268 100644
--- a/src/Sekiban.Core/Command/ICommandHandler.cs
+++ b/src/Sekiban.Core/Command/ICommandHandler.cs
@@ -10,7 +10,7 @@ namespace Sekiban.Core.Command;
 /// <typeparam name="TAggregatePayload">Target Aggregate</typeparam>
 /// <typeparam name="TCommand">Target Command</typeparam>
 public interface ICommandHandler<TAggregatePayload, TCommand> : ICommandHandlerCommon<TAggregatePayload, TCommand>
-    where TAggregatePayload : IAggregatePayloadCommon where TCommand : ICommand<TAggregatePayload>
+    where TAggregatePayload : IAggregatePayloadCommon, new() where TCommand : ICommand<TAggregatePayload>
 {
     /// <summary>
     ///     A Command Handler.
diff --git a/src/Sekiban.Core/Command/IVersionValidationCommandHandler.cs b/src/Sekiban.Core/Command/IVersionValidationCommandHandler.cs
index 0b7110f9..2e0e3765 100644
--- a/src/Sekiban.Core/Command/IVersionValidationCommandHandler.cs
+++ b/src/Sekiban.Core/Command/IVersionValidationCommandHandler.cs
@@ -10,4 +10,4 @@ namespace Sekiban.Core.Command;
 /// <typeparam name="TAggregatePayload"></typeparam>
 /// <typeparam name="TCommand"></typeparam>
 public interface IVersionValidationCommandHandler<TAggregatePayload, TCommand> : ICommandHandler<TAggregatePayload, TCommand>
-    where TAggregatePayload : IAggregatePayloadCommon where TCommand : IVersionValidationCommand<TAggregatePayload>;
+    where TAggregatePayload : IAggregatePayloadCommon, new() where TCommand : IVersionValidationCommand<TAggregatePayload>;
diff --git a/src/Sekiban.Core/Dependency/AggregateDependencyDefinition.cs b/src/Sekiban.Core/Dependency/AggregateDependencyDefinition.cs
index 53376aa0..c86c085e 100644
--- a/src/Sekiban.Core/Dependency/AggregateDependencyDefinition.cs
+++ b/src/Sekiban.Core/Dependency/AggregateDependencyDefinition.cs
@@ -88,13 +88,13 @@ public class AggregateDependencyDefinition<TAggregatePayload> : IAggregateDepend
     /// <summary>
     ///     Add Command Handler to Aggregate
     /// </summary>
-    /// <typeparam name="TCreateCommand">Target Command</typeparam>
+    /// <typeparam name="TCommand">Target Command</typeparam>
     /// <typeparam name="TCommandHandler">Command Handler for Target Command</typeparam>
     /// <returns>Self for method chain</returns>
-    public AggregateDependencyDefinition<TAggregatePayload> AddCommandHandler<TCreateCommand, TCommandHandler>()
-        where TCreateCommand : ICommand<TAggregatePayload>, new() where TCommandHandler : ICommandHandlerCommon<TAggregatePayload, TCreateCommand>
+    public AggregateDependencyDefinition<TAggregatePayload> AddCommandHandler<TCommand, TCommandHandler>()
+        where TCommand : ICommand<TAggregatePayload> where TCommandHandler : ICommandHandlerCommon<TAggregatePayload, TCommand>
     {
-        SelfCommandTypes = SelfCommandTypes.Add((typeof(ICommandHandlerCommon<TAggregatePayload, TCreateCommand>), typeof(TCommandHandler)));
+        SelfCommandTypes = SelfCommandTypes.Add((typeof(ICommandHandlerCommon<TAggregatePayload, TCommand>), typeof(TCommandHandler)));
         return this;
     }
     /// <summary>
diff --git a/src/Sekiban.Core/Dependency/DomainDependencyDefinitionBase.cs b/src/Sekiban.Core/Dependency/DomainDependencyDefinitionBase.cs
index 0c9bf0dd..1843779c 100644
--- a/src/Sekiban.Core/Dependency/DomainDependencyDefinitionBase.cs
+++ b/src/Sekiban.Core/Dependency/DomainDependencyDefinitionBase.cs
@@ -21,11 +21,6 @@ public abstract class DomainDependencyDefinitionBase : IDependencyDefinition
     private ImmutableList<Type> GeneralListQueryTypes { get; set; } = ImmutableList<Type>.Empty;
     private ImmutableList<Assembly> Assemblies { get; set; } = ImmutableList<Assembly>.Empty;
     private ImmutableList<Action<IServiceCollection>> ServiceActions { get; set; } = ImmutableList<Action<IServiceCollection>>.Empty;
-    protected DomainDependencyDefinitionBase()
-    {
-        // ReSharper disable once VirtualMemberCallInConstructor
-        Define();
-    }
     public abstract Assembly GetExecutingAssembly();
 
     public IEnumerable<(Type serviceType, Type? implementationType)> GetCommandDependencies()
@@ -73,6 +68,8 @@ public virtual SekibanDependencyOptions GetSekibanDependencyOptions() =>
             new SekibanAggregateTypes(GetAssembliesForOptions()),
             GetCommandDependencies().Concat(GetSubscriberDependencies()));
 
+    public abstract void Define();
+
 
     public void AddServices(Action<IServiceCollection> serviceAction)
     {
@@ -94,8 +91,6 @@ public IEnumerable<Type> GetSingleProjectionTypes()
         return AggregateDefinitions.SelectMany(s => s.SingleProjectionTypes);
     }
 
-    protected abstract void Define();
-
     public IEnumerable<Type> GetAggregatePayloadTypes()
     {
         return AggregateDefinitions.Select(s => s.AggregateType);
diff --git a/src/Sekiban.Core/Dependency/IDependencyDefinition.cs b/src/Sekiban.Core/Dependency/IDependencyDefinition.cs
index 4f08dcae..b4c27e10 100644
--- a/src/Sekiban.Core/Dependency/IDependencyDefinition.cs
+++ b/src/Sekiban.Core/Dependency/IDependencyDefinition.cs
@@ -21,4 +21,5 @@ public SekibanDependencyOptions GetSekibanDependencyOptions() =>
     IEnumerable<(Type serviceType, Type? implementationType)> GetSubscriberDependencies();
     IEnumerable<Action<IServiceCollection>> GetServiceActions();
     IEnumerable<IAggregateDependencyDefinition> GetAggregateDefinitions();
+    void Define();
 }
diff --git a/src/Sekiban.Core/Dependency/SekibanEventSourcingDependency.cs b/src/Sekiban.Core/Dependency/SekibanEventSourcingDependency.cs
index ca582568..7a9167b4 100644
--- a/src/Sekiban.Core/Dependency/SekibanEventSourcingDependency.cs
+++ b/src/Sekiban.Core/Dependency/SekibanEventSourcingDependency.cs
@@ -48,7 +48,8 @@ public static void Register(
         services.AddSekibanCore(sekibanDateProducer ?? new SekibanDateProducer(), multiProjectionType, configuration);
         services.AddSekibanHTTPUser();
         services.AddSekibanSettingsFromAppSettings();
-
+        // run Define() before using.
+        dependencyDefinition.Define();
         // Each Domain contexts
         services.AddSingleton(dependencyDefinition.GetSekibanDependencyOptions().RegisteredEventTypes);
         services.AddSingleton(dependencyDefinition.GetSekibanDependencyOptions().SekibanAggregateTypes);
@@ -88,6 +89,8 @@ public static void RegisterForInMemoryTest(
         services.AddSekibanHTTPUser();
 
         services.AddSekibanSettingsFromAppSettings();
+        // run Define() before using.
+        dependencyDefinition.Define();
 
         // Each Domain contexts
         services.AddSingleton(dependencyDefinition.GetSekibanDependencyOptions().RegisteredEventTypes);
@@ -127,6 +130,8 @@ public static void RegisterForAggregateTest(
         services.AddSekibanHTTPUser();
 
         services.AddSekibanAppSettingsFromObject(new AggregateSettings());
+        // run Define() before using.
+        dependencyDefinition.Define();
 
         // Each Domain contexts
         services.AddSingleton(dependencyDefinition.GetSekibanDependencyOptions().RegisteredEventTypes);
diff --git a/src/Sekiban.Core/Events/RegisteredEventTypes.cs b/src/Sekiban.Core/Events/RegisteredEventTypes.cs
index ddd67ffb..d302645c 100644
--- a/src/Sekiban.Core/Events/RegisteredEventTypes.cs
+++ b/src/Sekiban.Core/Events/RegisteredEventTypes.cs
@@ -19,7 +19,8 @@ public RegisteredEventTypes(params Assembly[] assemblies)
     {
         foreach (var assembly in assemblies)
         {
-            var decoratedTypes = assembly.DefinedTypes.Where(x => x.IsClass && x.ImplementedInterfaces.Contains(typeof(IEventPayloadCommon)));
+            var types = assembly.DefinedTypes;
+            var decoratedTypes = types.Where(x => x.IsClass && x.ImplementedInterfaces.Contains(typeof(IEventPayloadCommon)));
             foreach (var type in decoratedTypes)
             {
                 if (_registeredTypes.Contains(type))
diff --git a/tools/Convert011To012/EmptyDependencyDefinition.cs b/tools/Convert011To012/EmptyDependencyDefinition.cs
index c6a2faaa..8abc28f3 100644
--- a/tools/Convert011To012/EmptyDependencyDefinition.cs
+++ b/tools/Convert011To012/EmptyDependencyDefinition.cs
@@ -13,7 +13,7 @@ public class EmptyDependencyDefinition : DomainDependencyDefinitionBase, IWebDep
     public SekibanControllerOptions Options => new();
 
     public override Assembly GetExecutingAssembly() => Assembly.GetExecutingAssembly();
-    protected override void Define()
+    public override void Define()
     {
     }
 }