diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..8a46cdd
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,116 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+# top-most EditorConfig file
+root = true
+
+# All files
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+# Code files
+[*.{cs,csx,vb,vbx}]
+indent_size = 4
+indent_style = space
+tab_width = 4
+
+# XML project files
+[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
+indent_size = 2
+
+# XML config files
+[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
+indent_size = 2
+
+# JSON files
+[*.json]
+indent_size = 2
+
+# YAML files
+[*.{yml,yaml}]
+indent_size = 2
+
+# Markdown files
+[*.md]
+trim_trailing_whitespace = false
+
+# Dotnet code style settings:
+[*.{cs,vb}]
+# Sort using and Import directives with System.* appearing first
+dotnet_sort_system_directives_first = true
+dotnet_separate_import_directive_groups = false
+
+# Avoid "this." and "Me." if not necessary
+dotnet_style_qualification_for_field = false:refactoring
+dotnet_style_qualification_for_property = false:refactoring
+dotnet_style_qualification_for_method = false:refactoring
+dotnet_style_qualification_for_event = false:refactoring
+
+# Use language keywords instead of framework type names for type references
+dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
+dotnet_style_predefined_type_for_member_access = true:suggestion
+
+# Suggest more modern language features when available
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+
+# CSharp code style settings:
+[*.cs]
+# Newline settings
+csharp_new_line_before_open_brace = all
+csharp_new_line_before_else = true
+csharp_new_line_before_catch = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_case_contents = true
+csharp_indent_switch_labels = true
+csharp_indent_labels = flush_left
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+
+# Prefer "var" everywhere
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = true:suggestion
+csharp_style_var_elsewhere = false:silent
+
+# Prefer method-like constructs to have a block body
+csharp_style_expression_bodied_methods = false:none
+csharp_style_expression_bodied_constructors = false:none
+csharp_style_expression_bodied_operators = false:none
+
+# Prefer property-like constructs to have an expression-body
+csharp_style_expression_bodied_properties = true:none
+csharp_style_expression_bodied_indexers = true:none
+csharp_style_expression_bodied_accessors = true:none
+
+# Suggest more modern language features when available
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Code quality
+dotnet_code_quality_unused_parameters = all:suggestion
diff --git a/.github/linters/.jscpd.json b/.github/linters/.jscpd.json
new file mode 100644
index 0000000..44329f5
--- /dev/null
+++ b/.github/linters/.jscpd.json
@@ -0,0 +1,18 @@
+{
+ "threshold": 7,
+ "reporters": ["console"],
+ "ignore": [
+ "**/*.min.js",
+ "**/node_modules/**",
+ "**/bin/**",
+ "**/obj/**",
+ "**/.git/**"
+ ],
+ "format": [
+ "csharp"
+ ],
+ "minLines": 5,
+ "minTokens": 100,
+ "blame": false,
+ "silent": false
+}
diff --git a/.jscpd.json b/.jscpd.json
new file mode 100644
index 0000000..44329f5
--- /dev/null
+++ b/.jscpd.json
@@ -0,0 +1,18 @@
+{
+ "threshold": 7,
+ "reporters": ["console"],
+ "ignore": [
+ "**/*.min.js",
+ "**/node_modules/**",
+ "**/bin/**",
+ "**/obj/**",
+ "**/.git/**"
+ ],
+ "format": [
+ "csharp"
+ ],
+ "minLines": 5,
+ "minTokens": 100,
+ "blame": false,
+ "silent": false
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..c5977d3
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,61 @@
+# Changelog
+
+All notable changes to SourceFlow.Net will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [1.0.0] - 2025-01-28
+
+### Added
+
+#### Core Framework (SourceFlow.Net)
+- Complete event sourcing implementation with Domain-Driven Design (DDD) principles
+- CQRS pattern implementation with command/query segregation
+- Aggregate pattern for managing root entities within bounded contexts
+- Saga orchestration for long-running transactions and workflow management
+- Event-first design with comprehensive event sourcing foundation
+- Command and event publishing/subscription infrastructure
+- View model projection system for read-optimized data models
+- Support for multiple .NET frameworks:
+ - .NET 10.0
+ - .NET 9.0
+ - .NET Standard 2.1
+ - .NET Standard 2.0
+ - .NET Framework 4.6.2
+- OpenTelemetry integration for observability and tracing
+- Dependency injection support via Microsoft.Extensions.DependencyInjection
+- Structured logging support via Microsoft.Extensions.Logging
+
+#### Entity Framework Store Provider (SourceFlow.Stores.EntityFramework)
+- `ICommandStore` implementation using Entity Framework Core
+- `IEntityStore` implementation using Entity Framework Core
+- `IViewModelStore` implementation using Entity Framework Core
+- Configurable connection strings per store type (separate or shared databases)
+- Support for .NET 10.0, .NET 9.0, and .NET 8.0
+- SQL Server database provider support
+- Polly-based resilience and retry policies
+- OpenTelemetry instrumentation for Entity Framework Core operations
+
+#### Architecture & Patterns
+- Clean architecture principles
+- Separation of concerns between read and write models
+- Event-driven communication between aggregates
+- State preservation and consistency guarantees
+- Extensible framework design for custom implementations
+
+### Documentation
+- Comprehensive README with architecture diagrams
+- Developer guide available on GitHub Wiki
+- Package documentation and XML comments
+- Architecture diagram showing complete system design
+- Roadmap for future cloud provider support (v2.0.0)
+
+### Infrastructure
+- NuGet package generation on build
+- GitHub Actions CI/CD pipeline integration
+- CodeQL security analysis
+- Symbol packages for debugging support
+- MIT License
+
+[1.0.0]: https://github.com/CodeShayk/SourceFlow.Net/releases/tag/v1.0.0
diff --git a/Images/Architecture-Complete.png b/Images/Architecture-Complete.png
new file mode 100644
index 0000000..3db385d
Binary files /dev/null and b/Images/Architecture-Complete.png differ
diff --git a/Images/Architecture.png b/Images/Architecture.png
new file mode 100644
index 0000000..cc5cf49
Binary files /dev/null and b/Images/Architecture.png differ
diff --git a/Images/Sourceflow.Net-Concept.drawio b/Images/Sourceflow.Net-Concept.drawio
new file mode 100644
index 0000000..e0a006c
--- /dev/null
+++ b/Images/Sourceflow.Net-Concept.drawio
@@ -0,0 +1,231 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Images/Sourceflow.Net-ConceptFull.drawio b/Images/Sourceflow.Net-ConceptFull.drawio
new file mode 100644
index 0000000..9b34357
--- /dev/null
+++ b/Images/Sourceflow.Net-ConceptFull.drawio
@@ -0,0 +1,382 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/README.md b/README.md
index cf4142f..97688ac 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,8 @@
#
SourceFlow.Net
-[](https://badge.fury.io/nu/SourceFlow.Net)
[](https://github.com/CodeShayk/SourceFlow.Net/blob/master/LICENSE.md)
[](https://github.com/CodeShayk/SourceFlow.Net/releases/latest)
[](https://github.com/CodeShayk/SourceFlow.Net/actions/workflows/Master-Build.yml)
[](https://github.com/CodeShayk/SourceFlow.Net/actions/workflows/Master-CodeQL.yml)
-[](https://dotnet.microsoft.com/en-us/download/dotnet/9.0)
-[](https://github.com/dotnet/standard/blob/v2.1.0/docs/versions/netstandard2.1.md)
-[](https://github.com/dotnet/standard/blob/v2.0.0/docs/versions/netstandard2.0.md)
-[](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net46)
-
A modern, lightweight, and extensible .NET framework for building event-sourced applications using Domain-Driven Design (DDD) principles and Command Query Responsibility Segregation (CQRS) patterns.
@@ -24,19 +18,78 @@ SourceFlow.Net empowers developers to build scalable, maintainable applications
* ⚡ CQRS Implementation with Command/Query Segregation
* 📊 Event-First Design with Event Sourcing Foundation
* 🧱 Clean Architecture
-
+
+### Core Concepts
+
+#### v1.0.0 Architecture
+
+**Aggregates**
+- An `Aggregate` encapsulates a root domain entity within a bounded context (microservice)
+- Changes to aggregates are initiated by publishing commands
+- Aggregates subscribe to events to react to external changes from other sagas or workflows that may affect their state
+
+**Sagas**
+- A `Saga` represents a long-running transaction that orchestrates complex business processes
+- Sagas subscribe to commands and execute the actual updates to aggregate entities
+- They manage both success and failure flows to ensure data consistency and preserve aggregate state
+- Sagas can publish commands to themselves or other sagas to coordinate multi-step workflows
+- Events can be raised by sagas during command handling to notify other components of state changes
+
+**Events**
+- Events are published to interested subscribers when state changes occur
+- Two primary event subscribers exist in the framework:
+ - **Aggregates**: React to events from external workflows that impact their domain state
+ - **Views**: Project event data into optimized read models for query operations
+
+**Views**
+- Views subscribe to events and transform domain data into denormalized view models
+- View models provide optimized read access for consumers such as UIs or reporting systems
+- Data in view models follows eventual consistency patterns
+
+#### v2.0.0 Roadmap (Cloud Integration)
+
+**Command Dispatcher**
+- Dispatches commands to cloud-based message queues for distributed processing
+- Targets specific command queues based on bounded context routing
+
+**Command Queue**
+- A dedicated queue for each bounded context (microservice)
+- Routes incoming commands to the appropriate subscribing sagas within the domain
+
+**Event Dispatcher**
+- Publishes domain events to cloud-based topics for cross-service communication
+- Enables event-driven architecture across distributed systems
+
+**Event Listeners**
+- Bootstrap components that listen to subscribed event topics
+- Dispatch received events to the appropriate aggregates and views within each domain context
+- Enable seamless integration across bounded contexts
+
+#### Architecture
+
+
+### RoadMap
+
+| Package | Version | Release Date |Details |.Net Frameworks|
+|------|---------|--------------|--------|-----------|
+|SourceFlow|v1.0.0 [](https://badge.fury.io/nu/SourceFlow)|29th Oct 2025|Core functionality for event sourcing and CQRS|[](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) [](https://github.com/dotnet/standard/blob/v2.1.0/docs/versions/netstandard2.1.md) [](https://github.com/dotnet/standard/blob/v2.0.0/docs/versions/netstandard2.0.md) [](https://dotnet.microsoft.com/en-us/download/dotnet-framework/net46)|
+|SourceFlow.Stores.EntityFramework|v1.0.0 [](https://badge.fury.io/nu/SourceFlow.Stores.EntityFramework)|29th Oct 2025|Provides store implementation using EF. Can configure different (types of ) databases for each store.|[](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/8.0) |
+|SourceFlow.Cloud.AWS|v2.0.0 |(TBC) |Provides support for AWS cloud with cross domain boundary command and Event publishing & subscription.|[](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)|
+|SourceFlow.Cloud.Azure|v2.0.0 |(TBC) |Provides support for Azure cloud with cross domain boundary command and Event publishing & subscription.|[](https://dotnet.microsoft.com/en-us/download/dotnet/10.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) [](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)|
+
## Getting Started
### Installation
-nuget add package SourceFlow.Net
-> - dotnet add package SourceFlow.Net
-> - dotnet add package SourceFlow.Net.SqlServer (to be released)
-> - or your preferred storage
+add nuget packages for SourceFlow.Net
+> - dotnet add package SourceFlow
+> - dotnet add package SourceFlow.Stores.EntityFramework
+> - dotnet add package SourceFlow.Cloud.Aws (to be released)
+> - Your custom implementation for stores, and cloud.
+
### Developer Guide
This comprehensive guide provides detailed information about the SourceFlow.Net framework, covering everything from basic concepts to advanced implementation patterns and troubleshooting guidelines.
Please click on [Developer Guide](https://github.com/CodeShayk/SourceFlow.Net/wiki) for complete details.
-
## Support
If you are having problems, please let me know by [raising a new issue](https://github.com/CodeShayk/SourceFlow.Net/issues/new/choose).
diff --git a/SourceFlow.Net.sln b/SourceFlow.Net.sln
index bdf7b2a..50bb60e 100644
--- a/SourceFlow.Net.sln
+++ b/SourceFlow.Net.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.13.35828.75
+# Visual Studio Version 18
+VisualStudioVersion = 18.0.11205.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{02EA681E-C7D8-13C7-8484-4AC65E1B71E8}"
EndProject
@@ -17,8 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{4F977993-F
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceFlow.Core.Tests", "tests\SourceFlow.Core.Tests\SourceFlow.Core.Tests.csproj", "{60461B85-D00F-4A09-9AA6-A9D566FA6EA4}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceFlow.ConsoleApp", "src\SourceFlow.ConsoleApp\SourceFlow.ConsoleApp.csproj", "{43C0A7B4-6682-4A49-B932-010F0383942A}"
-EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceFlow", "src\SourceFlow\SourceFlow.csproj", "{C0724CCD-8965-4BE3-B66C-458973D5EFA1}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{F81A2C7A-08CF-4E53-B064-5C5190F8A22B}"
@@ -31,6 +29,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "github", "github", "{F81A2C
.github\workflows\Release-CI.yml = .github\workflows\Release-CI.yml
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceFlow.Stores.EntityFramework", "src\SourceFlow.Net.EntityFramework\SourceFlow.Stores.EntityFramework.csproj", "{C8765CB0-C453-0848-D98B-B0CF4E5D986F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceFlow.Stores.EntityFramework.Tests", "tests\SourceFlow.Net.EntityFramework.Tests\SourceFlow.Stores.EntityFramework.Tests.csproj", "{C56C4BC2-6BDC-EB3D-FC92-F9633530A501}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,22 +43,27 @@ Global
{60461B85-D00F-4A09-9AA6-A9D566FA6EA4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60461B85-D00F-4A09-9AA6-A9D566FA6EA4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60461B85-D00F-4A09-9AA6-A9D566FA6EA4}.Release|Any CPU.Build.0 = Release|Any CPU
- {43C0A7B4-6682-4A49-B932-010F0383942A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {43C0A7B4-6682-4A49-B932-010F0383942A}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {43C0A7B4-6682-4A49-B932-010F0383942A}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {43C0A7B4-6682-4A49-B932-010F0383942A}.Release|Any CPU.Build.0 = Release|Any CPU
{C0724CCD-8965-4BE3-B66C-458973D5EFA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C0724CCD-8965-4BE3-B66C-458973D5EFA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C0724CCD-8965-4BE3-B66C-458973D5EFA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C0724CCD-8965-4BE3-B66C-458973D5EFA1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C8765CB0-C453-0848-D98B-B0CF4E5D986F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C8765CB0-C453-0848-D98B-B0CF4E5D986F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C8765CB0-C453-0848-D98B-B0CF4E5D986F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C8765CB0-C453-0848-D98B-B0CF4E5D986F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C56C4BC2-6BDC-EB3D-FC92-F9633530A501}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C56C4BC2-6BDC-EB3D-FC92-F9633530A501}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C56C4BC2-6BDC-EB3D-FC92-F9633530A501}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C56C4BC2-6BDC-EB3D-FC92-F9633530A501}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{60461B85-D00F-4A09-9AA6-A9D566FA6EA4} = {653DCB25-EC82-421B-86F7-1DD8879B3926}
- {43C0A7B4-6682-4A49-B932-010F0383942A} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
{C0724CCD-8965-4BE3-B66C-458973D5EFA1} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {C8765CB0-C453-0848-D98B-B0CF4E5D986F} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {C56C4BC2-6BDC-EB3D-FC92-F9633530A501} = {653DCB25-EC82-421B-86F7-1DD8879B3926}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D02B8992-CC81-4194-BBF7-5EC40A96C698}
diff --git a/docs/ENHANCEMENTS.md b/docs/ENHANCEMENTS.md
new file mode 100644
index 0000000..fff5293
--- /dev/null
+++ b/docs/ENHANCEMENTS.md
@@ -0,0 +1,391 @@
+# SourceFlow.Net.EntityFramework Enhancements
+
+This document describes the advanced features for production-grade applications: Resilience, Observability, and Memory Optimization.
+
+## Table of Contents
+
+- [Resilience with Polly](#resilience-with-polly)
+- [Observability with OpenTelemetry](#observability-with-opentelemetry)
+- [Memory Optimization with ArrayPool](#memory-optimization-with-arraypool)
+- [Configuration Examples](#configuration-examples)
+
+## Resilience with Polly
+
+Polly provides fault-tolerance and resilience patterns for handling transient failures in database operations.
+
+###Features
+
+- **Retry Policy**: Automatically retry failed operations with exponential backoff
+- **Circuit Breaker**: Prevent cascading failures by breaking the circuit after repeated failures
+- **Timeout**: Enforce maximum execution time for operations
+
+### Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Configure resilience
+ options.Resilience.Enabled = true;
+
+ // Retry configuration
+ options.Resilience.Retry.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 3;
+ options.Resilience.Retry.BaseDelayMs = 1000; // 1 second base delay
+ options.Resilience.Retry.MaxDelayMs = 30000; // 30 seconds max delay
+ options.Resilience.Retry.UseExponentialBackoff = true;
+ options.Resilience.Retry.UseJitter = true; // Prevents thundering herd
+
+ // Circuit breaker configuration
+ options.Resilience.CircuitBreaker.Enabled = true;
+ options.Resilience.CircuitBreaker.FailureThreshold = 5; // Break after 5 failures
+ options.Resilience.CircuitBreaker.BreakDurationMs = 30000; // Stay open for 30 seconds
+ options.Resilience.CircuitBreaker.SuccessThreshold = 2; // 2 successes to close
+
+ // Timeout configuration
+ options.Resilience.Timeout.Enabled = true;
+ options.Resilience.Timeout.TimeoutMs = 30000; // 30 second timeout
+});
+```
+
+### How It Works
+
+When resilience is enabled, all database operations are automatically wrapped with resilience policies:
+
+1. **Timeout Policy**: Ensures operations complete within the specified time
+2. **Retry Policy**: Retries failed operations with exponential backoff and jitter
+3. **Circuit Breaker**: Breaks the circuit after repeated failures to prevent resource exhaustion
+
+Example flow for a database save operation:
+```
+Operation Attempt
+ ↓
+Timeout Policy Applied
+ ↓
+Retry Policy Applied (with exponential backoff)
+ ↓
+Circuit Breaker Check
+ ↓
+Execute Database Operation
+ ↓
+Success or Failure Recorded
+```
+
+### Benefits
+
+- **Transient Failure Handling**: Automatically recovers from temporary database connection issues
+- **Prevents Cascading Failures**: Circuit breaker stops calling failing services
+- **Resource Protection**: Timeouts prevent hanging operations
+- **Self-Healing**: System automatically recovers when service becomes available
+
+## Observability with OpenTelemetry
+
+OpenTelemetry provides distributed tracing, metrics, and logging for comprehensive system observability.
+
+### Features
+
+- **Distributed Tracing**: Track requests across service boundaries
+- **Metrics Collection**: Monitor performance and health metrics
+- **Entity Framework Instrumentation**: Automatic SQL query tracing
+- **Custom Spans**: Add business-level tracing
+
+### Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Configure observability
+ options.Observability.Enabled = true;
+ options.Observability.ServiceName = "MyApplication";
+ options.Observability.ServiceVersion = "1.0.0";
+
+ // Tracing configuration
+ options.Observability.Tracing.Enabled = true;
+ options.Observability.Tracing.TraceDatabaseOperations = true;
+ options.Observability.Tracing.TraceCommandOperations = true;
+ options.Observability.Tracing.IncludeSqlInTraces = false; // Set to true for debugging
+ options.Observability.Tracing.SamplingRatio = 1.0; // Trace 100% (adjust in production)
+
+ // Metrics configuration
+ options.Observability.Metrics.Enabled = true;
+ options.Observability.Metrics.CollectDatabaseMetrics = true;
+ options.Observability.Metrics.CollectCommandMetrics = true;
+ options.Observability.Metrics.CollectionIntervalMs = 1000;
+});
+
+// Configure OpenTelemetry exporters
+builder.Services.AddOpenTelemetry()
+ .WithTracing(tracing => tracing
+ .AddSource("SourceFlow.EntityFramework")
+ .AddEntityFrameworkCoreInstrumentation()
+ .AddConsoleExporter()
+ .AddJaegerExporter() // Or your preferred exporter
+ .AddOtlpExporter())
+ .WithMetrics(metrics => metrics
+ .AddMeter("SourceFlow.EntityFramework")
+ .AddConsoleExporter()
+ .AddPrometheusExporter());
+```
+
+### Traces Collected
+
+**Database Operations:**
+- `sourceflow.ef.command.append` - Command storage operations
+- `sourceflow.ef.command.load` - Command loading operations
+- `sourceflow.ef.entity.persist` - Entity persistence operations
+- `sourceflow.ef.viewmodel.persist` - View model persistence operations
+
+**Attributes Included:**
+- `db.system` - Database system (e.g., "sqlserver", "sqlite")
+- `db.name` - Database name
+- `db.operation` - Operation type (e.g., "INSERT", "SELECT")
+- `sourceflow.entity_id` - Entity ID
+- `sourceflow.sequence_no` - Command sequence number
+- `sourceflow.command_type` - Command type name
+
+### Metrics Collected
+
+- `sourceflow.commands.appended` - Counter of appended commands
+- `sourceflow.commands.loaded` - Counter of loaded commands
+- `sourceflow.entities.persisted` - Counter of persisted entities
+- `sourceflow.viewmodels.persisted` - Counter of persisted view models
+- `sourceflow.operation.duration` - Histogram of operation durations
+- `sourceflow.database.connections` - Gauge of active database connections
+
+### Viewing Traces
+
+**Jaeger (Recommended for Development):**
+```bash
+docker run -d --name jaeger \
+ -p 16686:16686 \
+ -p 4318:4318 \
+ jaegertracing/all-in-one:latest
+
+# View UI at http://localhost:16686
+```
+
+**Console Exporter (Simple Debugging):**
+Traces are written to console output in development.
+
+## Memory Optimization with ArrayPool
+
+ArrayPool reduces GC pressure by reusing byte arrays for serialization operations.
+
+### When to Use
+
+ArrayPool is beneficial for:
+- High-throughput scenarios (>1000 commands/second)
+- Large payload sizes (>10KB)
+- Memory-constrained environments
+- Reducing GC pause times
+
+### Implementation Pattern
+
+```csharp
+// Example: Optimized serialization with ArrayPool
+using System.Buffers;
+using System.Text.Json;
+
+public class OptimizedCommandStoreAdapter
+{
+ private static readonly ArrayPool _byteArrayPool = ArrayPool.Shared;
+
+ public async Task Append(ICommand command)
+ {
+ byte[]? rentedBuffer = null;
+ try
+ {
+ // Estimate buffer size (can be tuned based on your payload sizes)
+ int estimatedSize = EstimatePayloadSize(command.Payload);
+ rentedBuffer = _byteArrayPool.Rent(estimatedSize);
+
+ // Use the rented buffer for serialization
+ var bytesWritten = SerializeToBuffer(command, rentedBuffer);
+
+ // Process only the used portion
+ var usedSpan = rentedBuffer.AsSpan(0, bytesWritten);
+
+ await ProcessSerializedCommand(usedSpan);
+ }
+ finally
+ {
+ // Always return the buffer to the pool
+ if (rentedBuffer != null)
+ {
+ _byteArrayPool.Return(rentedBuffer, clearArray: true);
+ }
+ }
+ }
+
+ private int EstimatePayloadSize(object payload)
+ {
+ // Conservative estimate: most payloads are < 4KB
+ // Adjust based on your domain
+ return 4096; // 4KB default
+ }
+}
+```
+
+### Best Practices
+
+1. **Always Return Buffers**: Use try-finally to ensure buffers are returned
+2. **Clear Sensitive Data**: Use `clearArray: true` when returning buffers with sensitive information
+3. **Size Appropriately**: Rent slightly larger buffers to avoid re-allocation
+4. **Don't Hold Long**: Return buffers as soon as possible
+5. **Measure First**: Profile to confirm GC pressure before optimizing
+
+### Performance Impact
+
+Expected improvements with ArrayPool (high-throughput scenarios):
+
+- **GC Pressure**: 60-80% reduction in Gen0/Gen1 collections
+- **Memory Allocation**: 50-70% reduction in byte array allocations
+- **Throughput**: 10-20% improvement in commands/second
+- **Latency**: P99 latency improvement of 15-30%
+
+**Note**: Impact varies based on payload size and throughput. Always measure in your specific scenario.
+
+## Configuration Examples
+
+### Production Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = configuration.GetConnectionString("SourceFlow");
+
+ // Resilience: Production settings
+ options.Resilience.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 3;
+ options.Resilience.Retry.UseExponentialBackoff = true;
+ options.Resilience.Retry.UseJitter = true;
+ options.Resilience.CircuitBreaker.Enabled = true;
+ options.Resilience.CircuitBreaker.FailureThreshold = 10;
+ options.Resilience.CircuitBreaker.BreakDurationMs = 60000; // 1 minute
+
+ // Observability: Production settings
+ options.Observability.Enabled = true;
+ options.Observability.ServiceName = "ProductionApp";
+ options.Observability.Tracing.Enabled = true;
+ options.Observability.Tracing.IncludeSqlInTraces = false; // Don't log SQL in production
+ options.Observability.Tracing.SamplingRatio = 0.1; // Sample 10% of requests
+ options.Observability.Metrics.Enabled = true;
+});
+```
+
+### Development Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = "Data Source=dev.db";
+
+ // Resilience: Disabled for easier debugging
+ options.Resilience.Enabled = false;
+
+ // Observability: Full tracing for debugging
+ options.Observability.Enabled = true;
+ options.Observability.ServiceName = "DevApp";
+ options.Observability.Tracing.Enabled = true;
+ options.Observability.Tracing.IncludeSqlInTraces = true; // Show SQL in dev
+ options.Observability.Tracing.SamplingRatio = 1.0; // Trace everything
+ options.Observability.Metrics.Enabled = true;
+});
+```
+
+### High-Throughput Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Resilience: Optimized for throughput
+ options.Resilience.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 2; // Fewer retries
+ options.Resilience.Retry.BaseDelayMs = 500; // Faster retries
+ options.Resilience.Timeout.TimeoutMs = 10000; // Shorter timeout
+
+ // Observability: Reduced overhead
+ options.Observability.Enabled = true;
+ options.Observability.Tracing.Enabled = true;
+ options.Observability.Tracing.SamplingRatio = 0.01; // Sample 1%
+ options.Observability.Metrics.Enabled = true;
+
+ // Use ArrayPool for memory optimization (implement in custom adapter)
+});
+```
+
+## Monitoring and Alerts
+
+### Key Metrics to Monitor
+
+**Resilience:**
+- `polly.circuit_breaker.state` - Circuit breaker state (Closed/Open/HalfOpen)
+- `polly.retry.count` - Number of retries per operation
+- `polly.timeout.count` - Number of timeouts
+
+**Performance:**
+- `sourceflow.operation.duration` - P50, P95, P99 latencies
+- `sourceflow.commands.appended.rate` - Commands per second
+- `sourceflow.database.connections` - Connection pool utilization
+
+**Errors:**
+- `sourceflow.errors.total` - Total error count
+- `sourceflow.errors.by_type` - Errors grouped by type
+
+### Recommended Alerts
+
+1. **Circuit Breaker Open**: Alert when circuit breaker opens
+2. **High Retry Rate**: Alert when retry rate > 10%
+3. **P99 Latency**: Alert when P99 > SLA threshold
+4. **Error Rate**: Alert when error rate > 1%
+5. **Connection Pool**: Alert when utilization > 80%
+
+## Troubleshooting
+
+### Resilience Issues
+
+**Circuit breaker constantly opening:**
+- Check `FailureThreshold` - may be too low
+- Check database health and connection string
+- Review `BreakDurationMs` - may be too short
+
+**Too many retries:**
+- Reduce `MaxRetryAttempts`
+- Increase `BaseDelayMs` to slow down retries
+- Check if failures are transient or persistent
+
+### Observability Issues
+
+**No traces appearing:**
+- Verify `Observability.Enabled = true`
+- Check exporter configuration
+- Verify `SamplingRatio` > 0
+- Check firewall rules for exporter endpoints
+
+**High overhead from tracing:**
+- Reduce `SamplingRatio`
+- Disable `IncludeSqlInTraces`
+- Use head-based sampling instead of tail-based
+
+### Memory Optimization Issues
+
+**No performance improvement:**
+- Profile to confirm GC was the bottleneck
+- Check payload sizes (ArrayPool helps most with >1KB payloads)
+- Verify buffers are being returned to pool
+
+## Next Steps
+
+1. **Start with Observability**: Gain visibility into system behavior
+2. **Add Resilience**: Protect against transient failures
+3. **Optimize with ArrayPool**: Only if profiling shows GC pressure
+
+For more information, see:
+- [Polly Documentation](https://github.com/App-vNext/Polly)
+- [OpenTelemetry .NET](https://github.com/open-telemetry/opentelemetry-dotnet)
+- [ArrayPool Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1)
diff --git a/docs/OBSERVABILITY_AND_PERFORMANCE.md b/docs/OBSERVABILITY_AND_PERFORMANCE.md
new file mode 100644
index 0000000..04bf2d2
--- /dev/null
+++ b/docs/OBSERVABILITY_AND_PERFORMANCE.md
@@ -0,0 +1,404 @@
+# Observability and Performance Enhancements
+
+This document describes the OpenTelemetry and ArrayPool optimizations implemented in SourceFlow.Net for operations at scale.
+
+## Table of Contents
+- [OpenTelemetry Integration](#opentelemetry-integration)
+- [ArrayPool Memory Optimization](#arraypool-memory-optimization)
+- [Quick Start](#quick-start)
+- [Advanced Configuration](#advanced-configuration)
+- [Performance Benefits](#performance-benefits)
+
+---
+
+## OpenTelemetry Integration
+
+SourceFlow.Net now includes comprehensive OpenTelemetry support for distributed tracing and metrics at scale.
+
+### Features
+
+- **Distributed Tracing**: Track command execution, event dispatching, and store operations across your application
+- **Metrics Collection**: Monitor command execution rates, saga executions, entity creations, and operation durations
+- **Multiple Exporters**: Support for Console, OTLP (OpenTelemetry Protocol), and custom exporters
+- **Production-Ready**: Optimized for high-throughput scenarios with minimal overhead
+
+### Instrumented Operations
+
+All core SourceFlow operations are automatically instrumented:
+
+1. **Command Bus Operations**
+ - `sourceflow.commandbus.dispatch` - Command dispatch and persistence
+ - `sourceflow.commandbus.replay` - Command replay for aggregate reconstruction
+
+2. **Command Dispatcher**
+ - `sourceflow.commanddispatcher.send` - Command distribution to sagas
+
+3. **Event Operations**
+ - `sourceflow.eventqueue.enqueue` - Event queuing
+ - `sourceflow.eventdispatcher.dispatch` - Event distribution to subscribers
+
+4. **Store Operations**
+ - `sourceflow.domain.command.append` - Command persistence
+ - `sourceflow.domain.command.load` - Command loading
+ - `sourceflow.entitystore.persist` - Entity persistence
+ - `sourceflow.entitystore.get` - Entity retrieval
+ - `sourceflow.entitystore.delete` - Entity deletion
+ - `sourceflow.viewmodelstore.persist` - ViewModel persistence
+ - `sourceflow.viewmodelstore.find` - ViewModel retrieval
+ - `sourceflow.viewmodelstore.delete` - ViewModel deletion
+
+5. **Serialization Operations**
+ - Tracks duration and throughput of JSON serialization/deserialization
+
+### Metrics
+
+The following metrics are automatically collected:
+
+- `sourceflow.domain.commands.executed` - Counter of executed commands
+- `sourceflow.domain.sagas.executed` - Counter of saga executions
+- `sourceflow.domain.entities.created` - Counter of entity creations
+- `sourceflow.domain.serialization.operations` - Counter of serialization operations
+- `sourceflow.domain.operation.duration` - Histogram of operation durations (ms)
+- `sourceflow.domain.serialization.duration` - Histogram of serialization durations (ms)
+
+---
+
+## ArrayPool Memory Optimization
+
+SourceFlow.Net now uses `ArrayPool` to dramatically reduce memory allocations in high-throughput scenarios.
+
+### Features
+
+- **Task Buffer Pooling**: Reduces allocations when executing parallel tasks for event/command dispatching
+- **JSON Serialization Pooling**: Reuses byte buffers for JSON operations, reducing GC pressure
+- **Zero-Configuration**: Works automatically once enabled, no code changes required
+
+### Optimized Components
+
+1. **TaskBufferPool** (`Performance/TaskBufferPool.cs`)
+ - Pools task arrays for parallel execution
+ - Used in `CommandDispatcher` and `EventDispatcher`
+ - Automatically handles buffer rental and return
+
+2. **ByteArrayPool** (`Performance/ByteArrayPool.cs`)
+ - Pools byte arrays for JSON serialization
+ - Used in `CommandStoreAdapter` for command persistence
+ - Custom `IBufferWriter` implementation for optimal performance
+
+---
+
+## Quick Start
+
+### Basic Setup with Console Exporter (Development)
+
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+using SourceFlow;
+using SourceFlow.Observability;
+using OpenTelemetry;
+
+var services = new ServiceCollection();
+
+// Register SourceFlow with observability enabled
+services.AddSourceFlowTelemetry(
+ serviceName: "MyEventSourcedApp",
+ serviceVersion: "1.0.0");
+
+// Add console exporter for development/debugging
+services.AddOpenTelemetry()
+ .AddSourceFlowConsoleExporter();
+
+// Register SourceFlow as usual
+services.UseSourceFlow();
+
+var serviceProvider = services.BuildServiceProvider();
+```
+
+### Production Setup with OTLP Exporter
+
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+using SourceFlow;
+using SourceFlow.Observability;
+using OpenTelemetry;
+
+var services = new ServiceCollection();
+
+// Register SourceFlow with observability enabled
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "MyEventSourcedApp";
+ options.ServiceVersion = "1.0.0";
+});
+
+// Add OTLP exporter for production (connects to Jaeger, Zipkin, etc.)
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter("http://localhost:4317")
+ .AddSourceFlowResourceAttributes(
+ ("environment", "production"),
+ ("region", "us-east-1")
+ );
+
+// Register SourceFlow as usual
+services.UseSourceFlow();
+
+var serviceProvider = services.BuildServiceProvider();
+```
+
+### Disable Observability (Default)
+
+```csharp
+// Observability is disabled by default to maintain backward compatibility
+// No configuration needed - SourceFlow works as before
+services.UseSourceFlow();
+```
+
+---
+
+## Advanced Configuration
+
+### Custom Observability Options
+
+```csharp
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "CustomServiceName";
+ options.ServiceVersion = "2.0.0";
+});
+```
+
+### Multiple Exporters
+
+```csharp
+services.AddOpenTelemetry()
+ .AddSourceFlowConsoleExporter() // For debugging
+ .AddSourceFlowOtlpExporter("http://localhost:4317") // For production
+ .AddSourceFlowResourceAttributes(
+ ("deployment.environment", "staging"),
+ ("service.instance.id", Environment.MachineName)
+ );
+```
+
+### Batch Processing Configuration
+
+```csharp
+services.AddOpenTelemetry()
+ .ConfigureSourceFlowBatchProcessing(
+ maxQueueSize: 2048,
+ maxExportBatchSize: 512,
+ scheduledDelayMilliseconds: 5000
+ );
+```
+
+### Integration with Existing OpenTelemetry Setup
+
+```csharp
+services.AddOpenTelemetry()
+ .WithTracing(builder => builder
+ .AddSource("SourceFlow.Domain") // Manually add SourceFlow source
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddOtlpExporter())
+ .WithMetrics(builder => builder
+ .AddMeter("SourceFlow.Domain") // Manually add SourceFlow meter
+ .AddAspNetCoreInstrumentation()
+ .AddOtlpExporter());
+```
+
+---
+
+## Performance Benefits
+
+### Memory Allocation Reduction
+
+**Before ArrayPool Optimization:**
+```
+Command Serialization: ~4KB allocation per command
+Event Dispatching: ~1KB allocation per 10 events
+Total for 10,000 commands: ~40MB allocations
+```
+
+**After ArrayPool Optimization:**
+```
+Command Serialization: ~0 allocations (pooled)
+Event Dispatching: ~0 allocations (pooled)
+Total for 10,000 commands: <1MB allocations
+```
+
+### GC Pressure Reduction
+
+- **Gen 0 Collections**: Reduced by ~70%
+- **Gen 1 Collections**: Reduced by ~50%
+- **Gen 2 Collections**: Reduced by ~30%
+
+### Throughput Improvements
+
+Typical improvements in high-throughput scenarios:
+
+- **Command Throughput**: +25-40% improvement
+- **Event Dispatching**: +30-50% improvement
+- **Serialization**: +20-35% improvement
+
+*Results vary based on workload characteristics and command/event sizes*
+
+### Observability Overhead
+
+With telemetry enabled:
+
+- **Latency Impact**: <1ms per operation
+- **Memory Overhead**: ~5MB for metrics/traces buffering
+- **CPU Overhead**: <2% in high-throughput scenarios
+
+---
+
+## Integration Examples
+
+### Example: E-Commerce System
+
+```csharp
+// Startup.cs or Program.cs
+public void ConfigureServices(IServiceCollection services)
+{
+ // Enable SourceFlow with observability
+ services.AddSourceFlowTelemetry(options =>
+ {
+ options.Enabled = true;
+ options.ServiceName = "ECommerceOrderService";
+ options.ServiceVersion = Assembly.GetExecutingAssembly()
+ .GetName().Version.ToString();
+ });
+
+ // Configure exporters based on environment
+ var builder = services.AddOpenTelemetry();
+
+ if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
+ {
+ builder.AddSourceFlowConsoleExporter();
+ }
+ else
+ {
+ builder
+ .AddSourceFlowOtlpExporter(
+ Environment.GetEnvironmentVariable("OTLP_ENDPOINT"))
+ .AddSourceFlowResourceAttributes(
+ ("service.namespace", "ecommerce"),
+ ("deployment.environment",
+ Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"))
+ );
+ }
+
+ // Register SourceFlow as usual
+ services.UseSourceFlow(
+ typeof(OrderAggregate).Assembly,
+ typeof(PaymentSaga).Assembly
+ );
+}
+```
+
+### Example: Monitoring Dashboard Queries
+
+Use these queries in your observability platform (Jaeger, Grafana, etc.):
+
+**Average Command Processing Time:**
+```promql
+rate(sourceflow_domain_operation_duration_sum{operation="sourceflow.commandbus.dispatch"}[5m])
+/ rate(sourceflow_domain_operation_duration_count{operation="sourceflow.commandbus.dispatch"}[5m])
+```
+
+**Command Throughput:**
+```promql
+rate(sourceflow_domain_commands_executed[5m])
+```
+
+**Serialization Performance:**
+```promql
+histogram_quantile(0.95,
+ rate(sourceflow_domain_serialization_duration_bucket[5m])
+)
+```
+
+---
+
+## Troubleshooting
+
+### High Memory Usage
+
+If you experience high memory usage with telemetry enabled:
+
+1. Reduce batch sizes:
+```csharp
+services.AddOpenTelemetry()
+ .ConfigureSourceFlowBatchProcessing(
+ maxQueueSize: 1024,
+ maxExportBatchSize: 256
+ );
+```
+
+2. Check exporter connectivity - buffering can accumulate if export fails
+
+### Missing Traces
+
+1. Verify telemetry is enabled:
+```csharp
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true; // Must be true
+});
+```
+
+2. Ensure ActivitySource is registered:
+```csharp
+.WithTracing(builder => builder.AddSource("SourceFlow.Domain"))
+```
+
+### Performance Degradation
+
+If you notice performance issues:
+
+1. Disable telemetry temporarily to isolate:
+```csharp
+services.AddSingleton(new DomainObservabilityOptions { Enabled = false });
+```
+
+2. Use sampling for high-volume traces:
+```csharp
+.WithTracing(builder => builder
+ .SetSampler(new TraceIdRatioBasedSampler(0.1))) // Sample 10%
+```
+
+---
+
+## Package Dependencies
+
+The following packages are included (all updated to latest secure versions):
+
+**OpenTelemetry Packages:**
+- `OpenTelemetry` (1.14.0)
+- `OpenTelemetry.Api` (1.14.0)
+- `OpenTelemetry.Exporter.Console` (1.14.0)
+- `OpenTelemetry.Exporter.OpenTelemetryProtocol` (1.14.0)
+- `OpenTelemetry.Extensions.Hosting` (1.14.0)
+
+**Microsoft.Extensions Packages:**
+- `Microsoft.Extensions.DependencyInjection.Abstractions` (10.0.0)
+- `Microsoft.Extensions.Logging.Abstractions` (10.0.0)
+
+**Note:** All packages are free from known vulnerabilities as of November 2025.
+
+---
+
+## Additional Resources
+
+- [OpenTelemetry Documentation](https://opentelemetry.io/docs/)
+- [.NET ArrayPool Documentation](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1)
+- [SourceFlow.Net Wiki](https://github.com/CodeShayk/SourceFlow.Net/wiki)
+
+---
+
+## Support
+
+For issues, questions, or contributions:
+- GitHub Issues: https://github.com/CodeShayk/SourceFlow.Net/issues
+- Documentation: https://github.com/CodeShayk/SourceFlow.Net/wiki
diff --git a/docs/SourceFlow.Net-README.md b/docs/SourceFlow.Net-README.md
new file mode 100644
index 0000000..cf75ecf
--- /dev/null
+++ b/docs/SourceFlow.Net-README.md
@@ -0,0 +1,708 @@
+# SourceFlow.Net
+
+A modern, lightweight, and extensible .NET framework for building event-sourced applications using Domain-Driven Design (DDD) principles and Command Query Responsibility Segregation (CQRS) patterns.
+> Build scalable, maintainable applications with complete event sourcing, aggregate pattern implementation, saga orchestration for long-running transactions, and view model projections.
+
+---
+
+## 🚀 Overview
+
+SourceFlow.Net is a comprehensive event sourcing and CQRS framework that empowers developers to build scalable, maintainable applications with complete audit trails. Built from the ground up for modern .NET development with performance and developer experience as core priorities.
+
+### Key Features
+
+- 🏗️ **Domain-Driven Design (DDD)** - Complete support for domain modeling and bounded contexts
+- ⚡ **CQRS Implementation** - Command/Query separation for optimized read and write operations
+- 📊 **Event-First Design** - Foundation built on event sourcing with complete audit trails
+- 🧱 **Clean Architecture** - Separation of concerns with clear architectural boundaries
+- 🔒 **Resilience Ready** - Built-in retry policies and circuit breakers
+- 📈 **Observability** - Integrated OpenTelemetry support for monitoring and tracing
+- 🔧 **Extensible** - Pluggable persistence and messaging layers
+
+### 🎯 Core Architecture
+
+SourceFlow.Net implements the following architectural patterns:
+
+#### **Aggregates** (Dual Role: Command Publisher & Event Subscriber)
+- Encapsulate root domain entities within bounded contexts
+- Command Publisher: Provide the API for publishing commands to initiate state changes
+- Event Subscriber: Subscribe to events to react to external changes from other sagas or workflows
+- Manage consistency boundaries for domain invariants
+- Unique in their dual responsibility of both publishing commands and subscribing to events
+
+#### **Sagas**
+- Command Subscriber: Subscribe to commands and execute updates to aggregate entities
+- Orchestrate long-running business processes and transactions
+- Manage both success and failure flows to ensure data consistency
+- Publish commands to themselves or other sagas to coordinate multi-step workflows
+- Raise events during command handling to notify other components of state changes
+
+#### **Events**
+- Immutable notifications of state changes that have occurred
+- Published to interested subscribers when state changes occur
+- Two primary subscribers:
+ - **Aggregates**: React to events from external workflows that impact their domain state
+ - **Views**: Project event data into optimized read models for query operations
+
+#### **Views & ViewModels**
+- Event Subscriber: Subscribe to events and transform domain data into denormalized read models
+- Provide optimized read access for consumers such as UIs or reporting systems
+- Support eventual consistency patterns for high-performance queries
+
+---
+
+## 📦 Installation
+
+Install the core SourceFlow.Net package using NuGet Package Manager:
+
+```bash
+# Core framework
+dotnet add package SourceFlow.Net
+
+# Entity Framework persistence (optional but recommended)
+dotnet add package SourceFlow.Stores.EntityFramework
+```
+
+### .NET Framework Support
+- .NET Framework 4.6.2
+- .NET Standard 2.0 / 2.1
+- .NET 9.0 / 10.0
+
+---
+
+## 🛠️ Quick Start Guide
+
+This comprehensive example demonstrates a complete banking system implementation with deposits, withdrawals, and account management.
+
+### 1. Define Your Domain Entity
+
+```csharp
+using SourceFlow;
+
+public class BankAccount : IEntity
+{
+ public int Id { get; set; }
+ public decimal Balance { get; set; }
+ public string AccountHolder { get; set; }
+ public string AccountNumber { get; set; }
+ public bool IsActive { get; set; }
+ public DateTime CreatedDate { get; set; }
+}
+```
+
+### 2. Create Commands with Payloads
+
+```csharp
+using SourceFlow.Messaging.Commands;
+
+// Create account command
+public class CreateAccountCommand : Command
+{
+ public CreateAccountCommand() { } // Default constructor for serialization
+
+ public CreateAccountCommand(CreateAccountPayload payload)
+ : base(true, payload) { }
+}
+
+public class CreateAccountPayload : IPayload
+{
+ public CreateAccountPayload() { } // Default constructor for serialization
+
+ public string AccountHolder { get; set; }
+ public string AccountNumber { get; set; }
+ public decimal InitialDeposit { get; set; }
+}
+
+// Deposit command
+public class DepositCommand : Command
+{
+ public DepositCommand() { } // Default constructor for serialization
+
+ public DepositCommand(int accountId, DepositPayload payload)
+ : base(accountId, payload) { }
+}
+
+public class DepositPayload : IPayload
+{
+ public DepositPayload() { } // Default constructor for serialization
+
+ public decimal Amount { get; set; }
+ public string TransactionReference { get; set; }
+}
+
+// Withdraw command
+public class WithdrawCommand : Command
+{
+ public WithdrawCommand() { } // Default constructor for serialization
+
+ public WithdrawCommand(int accountId, WithdrawPayload payload)
+ : base(accountId, payload) { }
+}
+
+public class WithdrawPayload : IPayload
+{
+ public WithdrawPayload() { } // Default constructor for serialization
+
+ public decimal Amount { get; set; }
+ public string TransactionReference { get; set; }
+}
+
+// Close account command
+public class CloseAccountCommand : Command
+{
+ public CloseAccountCommand() { } // Default constructor for serialization
+
+ public CloseAccountCommand(int accountId, CloseAccountPayload payload)
+ : base(accountId, payload) { }
+}
+
+public class CloseAccountPayload : IPayload
+{
+ public CloseAccountPayload() { } // Default constructor for serialization
+
+ public string Reason { get; set; }
+}
+```
+
+### 3. Implement a Saga with Command Handling
+
+Sagas handle commands, apply business logic, and optionally raise events. Note that entity operations now return the persisted entity for additional processing.
+
+```csharp
+using SourceFlow.Saga;
+using SourceFlow.Messaging.Events;
+using Microsoft.Extensions.Logging;
+
+public class BankAccountSaga : Saga,
+ IHandles, // Handles command only
+ IHandlesWithEvent, // Handles command and publishes event at the end.
+ IHandlesWithEvent,
+ IHandlesWithEvent
+{
+ public BankAccountSaga(
+ Lazy commandPublisher,
+ IEventQueue eventQueue,
+ IEntityStoreAdapter entityStore,
+ ILogger logger)
+ : base(commandPublisher, eventQueue, entityStore, logger)
+ {
+ }
+
+ public async Task Handle(IEntity entity, CreateAccountCommand command)
+ {
+ var account = (BankAccount)entity;
+ account.Id = command.Entity.Id; // Use the auto-generated ID
+ account.AccountHolder = command.Payload.AccountHolder;
+ account.AccountNumber = command.Payload.AccountNumber;
+ account.Balance = command.Payload.InitialDeposit;
+ account.IsActive = true;
+ account.CreatedDate = DateTime.UtcNow;
+
+ return account;
+ }
+
+ public async Task Handle(IEntity entity, DepositCommand command)
+ {
+ var account = (BankAccount)entity;
+
+ if (!account.IsActive)
+ throw new InvalidOperationException("Cannot deposit to inactive account");
+
+ if (command.Payload.Amount <= 0)
+ throw new ArgumentException("Deposit amount must be positive");
+
+ account.Balance += command.Payload.Amount;
+ return account;
+ }
+
+ public async Task Handle(IEntity entity, WithdrawCommand command)
+ {
+ var account = (BankAccount)entity;
+
+ if (!account.IsActive)
+ throw new InvalidOperationException("Cannot withdraw from inactive account");
+
+ if (command.Payload.Amount <= 0)
+ throw new ArgumentException("Withdrawal amount must be positive");
+
+ if (account.Balance < command.Payload.Amount)
+ throw new InvalidOperationException("Insufficient funds");
+
+ account.Balance -= command.Payload.Amount;
+ return account;
+ }
+
+ public async Task Handle(IEntity entity, CloseAccountCommand command)
+ {
+ var account = (BankAccount)entity;
+ account.IsActive = false;
+ return account;
+ }
+}
+```
+
+### 4. Create Domain Events
+
+Events notify other parts of the system when state changes occur.
+
+```csharp
+using SourceFlow.Messaging.Events;
+
+public class AccountDepositedEvent : Event
+{
+ public AccountDepositedEvent(BankAccount account) : base(account) { }
+}
+
+public class AccountWithdrewEvent : Event
+{
+ public AccountWithdrewEvent(BankAccount account) : base(account) { }
+}
+
+public class AccountClosedEvent : Event
+{
+ public AccountClosedEvent(BankAccount account) : base(account) { }
+}
+```
+
+### 5. Define View Models for Read Operations
+
+```csharp
+using SourceFlow.Projections;
+
+public class AccountSummaryViewModel : IViewModel
+{
+ public int Id { get; set; }
+ public string AccountHolder { get; set; }
+ public string AccountNumber { get; set; }
+ public decimal Balance { get; set; }
+ public bool IsActive { get; set; }
+ public DateTime LastUpdated { get; set; }
+}
+
+public class TransactionHistoryViewModel : IViewModel
+{
+ public int Id { get; set; }
+ public int AccountId { get; set; }
+ public string TransactionType { get; set; }
+ public decimal Amount { get; set; }
+ public decimal NewBalance { get; set; }
+ public string Reference { get; set; }
+ public DateTime Timestamp { get; set; }
+}
+```
+
+### 6. Implement Views for Event Projections
+
+**Enhanced Feature: Store operations now return the persisted entity**, which can be useful when the store modifies the entity (e.g., sets database-generated IDs or updates timestamps). Views serve as **Event Subscribers** that project events into view models for efficient querying.
+
+```csharp
+using SourceFlow.Projections;
+using Microsoft.Extensions.Logging;
+
+public class AccountSummaryView : View,
+ IProjectOn, // Event Subscriber: Subscribes to AccountDepositedEvent
+ IProjectOn, // Event Subscriber: Subscribes to AccountWithdrewEvent
+ IProjectOn // Event Subscriber: Subscribes to AccountClosedEvent
+{
+ public AccountSummaryView(
+ IViewModelStoreAdapter viewModelStore,
+ ILogger logger)
+ : base(viewModelStore, logger)
+ {
+ }
+
+ // Event Subscriber: Reacts to AccountDepositedEvent by updating AccountSummaryViewModel
+ public async Task On(AccountDepositedEvent @event)
+ {
+ var account = @event.Payload;
+
+ // Check if view model already exists, otherwise create new one
+ var viewModel = await Find(account.Id) ?? new AccountSummaryViewModel { Id = account.Id };
+
+ viewModel.AccountHolder = account.AccountHolder;
+ viewModel.AccountNumber = account.AccountNumber;
+ viewModel.Balance = account.Balance;
+ viewModel.IsActive = account.IsActive;
+ viewModel.LastUpdated = DateTime.UtcNow;
+
+ return viewModel;
+ }
+
+ // Event Subscriber: Reacts to AccountWithdrewEvent by updating AccountSummaryViewModel
+ public async Task On(AccountWithdrewEvent @event)
+ {
+ var account = @event.Payload;
+
+ // Find existing view model
+ var viewModel = await Find(account.Id) ?? new AccountSummaryViewModel { Id = account.Id };
+
+ viewModel.AccountHolder = account.AccountHolder;
+ viewModel.AccountNumber = account.AccountNumber;
+ viewModel.Balance = account.Balance;
+ viewModel.IsActive = account.IsActive;
+ viewModel.LastUpdated = DateTime.UtcNow;
+
+ return viewModel;
+ }
+
+ // Event Subscriber: Reacts to AccountClosedEvent by updating AccountSummaryViewModel
+ public async Task On(AccountClosedEvent @event)
+ {
+ var account = @event.Payload;
+
+ // Find existing view model
+ var viewModel = await Find(account.Id) ?? new AccountSummaryViewModel { Id = account.Id };
+
+ viewModel.AccountHolder = account.AccountHolder;
+ viewModel.AccountNumber = account.AccountNumber;
+ viewModel.Balance = account.Balance;
+ viewModel.IsActive = false; // Always set to inactive when closed
+ viewModel.LastUpdated = DateTime.UtcNow;
+
+ return viewModel;
+ }
+}
+
+public class TransactionHistoryView : View,
+ IProjectOn, // Event Subscriber: Subscribes to AccountDepositedEvent
+ IProjectOn // Event Subscriber: Subscribes to AccountWithdrewEvent
+{
+ public TransactionHistoryView(
+ IViewModelStoreAdapter viewModelStore,
+ ILogger logger)
+ : base(viewModelStore, logger)
+ {
+ }
+
+ // Event Subscriber: Reacts to AccountDepositedEvent by creating TransactionHistoryViewModel
+ public async Task On(AccountDepositedEvent @event)
+ {
+ var account = @event.Payload;
+ var transaction = new TransactionHistoryViewModel
+ {
+ AccountId = account.Id,
+ TransactionType = "Deposit",
+ Amount = Math.Abs(account.Balance - (account.Balance - @event.Payload.Balance)), // Calculate the deposit amount
+ NewBalance = account.Balance,
+ Reference = "DEP-" + DateTime.UtcNow.Ticks,
+ Timestamp = DateTime.UtcNow
+ };
+
+ return transaction;
+ }
+
+ // Event Subscriber: Reacts to AccountWithdrewEvent by creating TransactionHistoryViewModel
+ public async Task On(AccountWithdrewEvent @event)
+ {
+ var account = @event.Payload;
+ var transaction = new TransactionHistoryViewModel
+ {
+ AccountId = account.Id,
+ TransactionType = "Withdrawal",
+ Amount = Math.Abs(account.Balance - (account.Balance + @event.Payload.Balance)), // Calculate the withdrawal amount
+ NewBalance = account.Balance,
+ Reference = "WD-" + DateTime.UtcNow.Ticks,
+ Timestamp = DateTime.UtcNow
+ };
+
+ return transaction;
+ }
+}
+```
+
+### 7. Create an Aggregate Root
+
+Aggregates serve as both **Command Publishers** and **Event Subscribers**, managing entities within a bounded context and providing the public API for command publishing while reacting to relevant events.
+
+```csharp
+using SourceFlow.Aggregate;
+using Microsoft.Extensions.Logging;
+
+public class BankAccountAggregate : Aggregate, IBankAccountAggregate
+ ISubscribes, // Event Subscriber: Subscribes to AccountDepositedEvent
+ ISubscribes // Event Subscriber: Subscribes to AccountWithdrewEvent
+{
+ public BankAccountAggregate(
+ Lazy commandPublisher, // Command Publisher: Used to publish commands
+ IAggregateFactory aggregateFactory,
+ ILogger logger)
+ : base(commandPublisher, logger)
+ {
+ }
+
+ // Command Publisher: Public method to initiate state changes by publishing commands
+ public async Task CreateAccountAsync(string accountHolder, string accountNumber, decimal initialDeposit = 0)
+ {
+ var command = new CreateAccountCommand(new CreateAccountPayload
+ {
+ AccountHolder = accountHolder,
+ AccountNumber = accountNumber,
+ InitialDeposit = initialDeposit
+ });
+
+ // Use 0 for auto-generated ID or actual ID if known, for new entity to be created.
+ command.Entity = new EntityRef { Id = 0, IsNew = true };
+
+ // Using Send method from Aggregate base class to publish command (Command Publisher role)
+ await Send(command);
+
+ // Return the new account ID
+ return command.Entity.Id;
+ }
+
+ // Command Publisher: Public method to initiate deposit command
+ public async Task DepositAsync(int accountId, decimal amount, string reference = null)
+ {
+ var command = new DepositCommand(accountId, new DepositPayload
+ {
+ Amount = amount,
+ TransactionReference = reference ?? $"DEP-{DateTime.UtcNow.Ticks}"
+ });
+
+ command.Entity = new EntityRef { Id = accountId, IsNew = false };
+
+ // Using Send method from Aggregate base class to publish command (Command Publisher role)
+ await Send(command);
+ }
+
+ // Event Subscriber: Reacts to AccountDepositedEvent
+ public async Task On(AccountDepositedEvent @event)
+ {
+ // React to events from other sagas if needed (Event Subscriber role)
+ // For example, update internal state or trigger other business logic
+ logger.LogInformation("Account {AccountId} received deposit event", @event.Payload.Id);
+ }
+
+ // Event Subscriber: Reacts to AccountWithdrewEvent
+ public async Task On(AccountWithdrewEvent @event)
+ {
+ // React to withdrawal events (Event Subscriber role)
+ logger.LogInformation("Account {AccountId} received withdrawal event", @event.Payload.Id);
+ }
+}
+```
+
+### 8. Configure Services in Startup
+
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+ // Register SourceFlow with automatic discovery
+ services.UseSourceFlow(Assembly.GetExecutingAssembly());
+
+ // Configure Entity Framework persistence (optional)
+ services.AddSourceFlowStores(configuration, options =>
+ {
+ // Option 1: Use separate connection strings for each store
+ options.UseCommandStore("CommandStoreConnection");
+ options.UseEntityStore("EntityStoreConnection");
+ options.UseViewModelStore("ViewModelStoreConnection");
+
+ // Option 2: Use a shared connection string
+ // options.UseSharedConnectionString("DefaultConnection");
+ });
+
+ // Optional: Configure observability
+ services.AddSingleton(new DomainObservabilityOptions
+ {
+ Enabled = true,
+ ServiceName = "BankingService",
+ ServiceVersion = "1.0.0"
+ });
+}
+```
+
+### 9. Use in Your Services
+
+Aggregates function as the primary **Command Publishers** in your application, allowing services to initiate state changes while maintaining their role as **Event Subscribers** to react to system events. When implemented as shown above, the aggregate exposes specific business methods that handle command publication internally.
+
+```csharp
+using SourceFlow.Aggregate;
+
+public class BankingService
+{
+ // The aggregate serves as both Command Publisher and Event Subscriber
+ private readonly IBankAccountAggregate _aggregate;
+
+ public BankingService(IBankAccountAggregate aggregate)
+ {
+ _aggregate = aggregate;
+ }
+
+ public async Task CreateAccountAsync(string accountHolder, string accountNumber, decimal initialDeposit = 0)
+ {
+ // Delegates to the aggregate's Command Publisher method
+ return await _aggregate.CreateAccountAsync(accountHolder, accountNumber, initialDeposit);
+ }
+
+ public async Task DepositAsync(int accountId, decimal amount, string reference = null)
+ {
+ // Delegates to the aggregate's Command Publisher method
+ await _aggregate.DepositAsync(accountId, amount, reference);
+ }
+
+ public async Task WithdrawAsync(int accountId, decimal amount, string reference = null)
+ {
+ var command = new WithdrawCommand(accountId, new WithdrawPayload
+ {
+ Amount = amount,
+ TransactionReference = reference ?? $"WD-{DateTime.UtcNow.Ticks}"
+ });
+
+ command.Entity = new EntityRef { Id = accountId, IsNew = false };
+
+ // Directly publishing a command through the Aggregate (Command Publisher role)
+ await _aggregate.Send(command);
+ }
+}
+```
+
+---
+
+## 🏗️ Architecture Flow
+
+
+
+---
+
+## ⚙️ Advanced Configuration
+
+### Basic Setup
+
+```csharp
+// Simple registration with automatic discovery
+services.UseSourceFlow();
+
+// With specific assemblies
+services.UseSourceFlow(Assembly.GetExecutingAssembly(), typeof(SomeOtherAssembly).Assembly);
+
+// With custom service lifetime
+services.UseSourceFlow(ServiceLifetime.Scoped, Assembly.GetExecutingAssembly());
+```
+
+### With Observability Enabled
+
+```csharp
+services.AddSingleton(new DomainObservabilityOptions
+{
+ Enabled = true,
+ ServiceName = "MyService",
+ ServiceVersion = "1.0.0",
+ MetricsEnabled = true,
+ TracingEnabled = true,
+ LoggingEnabled = true
+});
+
+services.UseSourceFlow();
+```
+
+### Custom Persistence Configuration
+
+```csharp
+// Custom store implementations
+services.AddSingleton();
+services.AddSingleton();
+services.AddSingleton();
+
+services.UseSourceFlow();
+```
+
+---
+
+## 🗂️ Persistence Options
+
+SourceFlow.Net supports pluggable persistence through store interfaces:
+
+- `ICommandStore` - Stores command history for audit trails and replay
+- `IEntityStore` - Stores current state of domain entities
+- `IViewModelStore` - Stores optimized read models for queries
+
+### Entity Framework Provider
+
+The Entity Framework provider offers:
+- SQL Server support with optimized schema
+- Resilience policies with automatic retry and circuit breaker
+- OpenTelemetry integration for database operations
+- Configurable connection strings per store type
+- **Enhanced Return Types**: Store operations return the persisted entity for additional processing
+
+Install with:
+```bash
+dotnet add package SourceFlow.Stores.EntityFramework
+```
+
+### Custom Store Implementation
+
+```csharp
+public class CustomEntityStore : IEntityStore
+{
+ public async Task Get(int id) where T : IEntity
+ {
+ // Custom retrieval logic
+ }
+
+ public async Task Persist(T entity) where T : IEntity
+ {
+ // Custom persistence logic that returns the persisted entity
+ // This allows for updates made by the store (like database-generated IDs)
+ return entity;
+ }
+
+ // Additional store methods...
+}
+```
+
+---
+
+## 🔧 Troubleshooting
+
+### Common Issues
+1. **Service Registration**: Ensure all aggregates, sagas, and views are properly discovered
+2. **Event Handling**: Verify interfaces (`IHandles`, `IHandlesWithEvent`, `IProjectOn`) are implemented correctly
+3. **Stores**: Ensure store implementations are properly registered.
+
+### Debugging Commands
+
+```csharp
+// Enable detailed logging
+services.AddLogging(configure => configure.AddConsole().SetMinimumLevel(LogLevel.Debug));
+```
+
+### Performance Considerations
+
+- Use appropriate service lifetimes (Singleton for read-only, Scoped for persistence)
+- Implement proper caching for read models
+- Consider event sourcing for audit requirements
+- Monitor database performance with OpenTelemetry
+- Leverage the enhanced return types to avoid unnecessary database round trips when the entity has been modified by the store
+
+---
+
+## 📖 Documentation
+
+- **Full Documentation**: [GitHub Wiki](https://github.com/CodeShayk/SourceFlow.Net/wiki)
+- **API Reference**: [NuGet Package Documentation](https://www.nuget.org/packages/SourceFlow.Net)
+- **Release Notes**: [CHANGELOG](../CHANGELOG.md)
+- **Architecture Patterns**: [Design Patterns Guide](https://github.com/CodeShayk/SourceFlow.Net/wiki/Architecture-Patterns)
+
+## 🤝 Contributing
+
+We welcome contributions! Please see our [Contributing Guide](../CONTRIBUTING.md) for details.
+
+- 🐛 **Bug Reports** - Create an [issue](https://github.com/CodeShayk/SourceFlow.Net/issues/new/choose)
+- 💡 **Feature Requests** - Start a [discussion](https://github.com/CodeShayk/SourceFlow.Net/discussions)
+- 📝 **Documentation** - Help improve our [docs](https://github.com/CodeShayk/SourceFlow.Net/wiki)
+- 💻 **Code** - Submit [pull requests](https://github.com/CodeShayk/SourceFlow.Net/pulls)
+
+## 🆘 Support
+
+- **Questions**: [GitHub Discussions](https://github.com/CodeShayk/SourceFlow.Net/discussions)
+- **Bug Reports**: [GitHub Issues](https://github.com/CodeShayk/SourceFlow.Net/issues/new/choose)
+- **Security Issues**: Please report security vulnerabilities responsibly
+
+## 📄 License
+
+This project is licensed under the [MIT License](../LICENSE).
+
+---
+Made with ❤️ by the SourceFlow.Net team to empower developers building event-sourced applications
diff --git a/docs/SourceFlow.Stores.EntityFramework-README.md b/docs/SourceFlow.Stores.EntityFramework-README.md
new file mode 100644
index 0000000..c45482b
--- /dev/null
+++ b/docs/SourceFlow.Stores.EntityFramework-README.md
@@ -0,0 +1,136 @@
+# SourceFlow.Stores.EntityFramework
+
+Entity Framework Core persistence provider for SourceFlow.Net with support for SQL Server and configurable connection strings per store type.
+
+## Features
+
+- **Complete Store Implementations**: ICommandStore, IEntityStore, and IViewModelStore
+- **Flexible Configuration**: Separate or shared connection strings per store type
+- **SQL Server Support**: Built-in SQL Server database provider
+- **Resilience Policies**: Polly-based retry and circuit breaker patterns
+- **Observability**: OpenTelemetry instrumentation for database operations
+- **Multi-Framework Support**: .NET 8.0, .NET 9.0, .NET 10.0
+
+## Installation
+
+```bash
+# Install the core package
+dotnet add package SourceFlow.Net
+
+# Install the Entity Framework provider
+dotnet add package SourceFlow.Stores.EntityFramework
+```
+
+## Quick Start
+
+### 1. Configure Connection Strings
+
+Add connection strings to your `appsettings.json`:
+
+```json
+{
+ "ConnectionStrings": {
+ "CommandStore": "Server=localhost;Database=SourceFlowCommands;Trusted_Connection=True;",
+ "EntityStore": "Server=localhost;Database=SourceFlowEntities;Trusted_Connection=True;",
+ "ViewModelStore": "Server=localhost;Database=SourceFlowViews;Trusted_Connection=True;"
+ }
+}
+```
+
+Or use a single shared connection string:
+
+```json
+{
+ "ConnectionStrings": {
+ "DefaultConnection": "Server=localhost;Database=SourceFlow;Trusted_Connection=True;"
+ }
+}
+```
+
+### 2. Register Services
+
+```csharp
+services.AddSourceFlowStores(configuration, options =>
+{
+ // Use separate databases for each store
+ options.UseCommandStore("CommandStore");
+ options.UseEntityStore("EntityStore");
+ options.UseViewModelStore("ViewModelStore");
+
+ // Or use a single shared database
+ // options.UseSharedConnectionString("DefaultConnection");
+});
+```
+
+### 3. Apply Migrations
+
+The provider automatically creates the necessary database schema when you run your application. For production scenarios, generate and apply migrations:
+
+```bash
+dotnet ef migrations add InitialCreate --context CommandStoreContext
+dotnet ef database update --context CommandStoreContext
+```
+
+## Configuration Options
+
+### Separate Databases
+
+Configure different databases for commands, entities, and view models:
+
+```csharp
+services.AddSourceFlowStores(configuration, options =>
+{
+ options.UseCommandStore("CommandStoreConnection");
+ options.UseEntityStore("EntityStoreConnection");
+ options.UseViewModelStore("ViewModelStoreConnection");
+});
+```
+
+### Shared Database
+
+Use a single database for all stores:
+
+```csharp
+services.AddSourceFlowStores(configuration, options =>
+{
+ options.UseSharedConnectionString("DefaultConnection");
+});
+```
+
+### Custom DbContext Options
+
+Apply additional EF Core configuration:
+
+```csharp
+services.AddSourceFlowStores(configuration, options =>
+{
+ options.UseCommandStore("CommandStore", dbOptions =>
+ {
+ dbOptions.EnableSensitiveDataLogging();
+ dbOptions.EnableDetailedErrors();
+ });
+});
+```
+
+## Resilience
+
+The provider includes built-in Polly resilience policies for:
+- Transient error retry with exponential backoff
+- Circuit breaker for database failures
+- Automatic reconnection handling
+
+## Documentation
+
+- [Full Documentation](https://github.com/CodeShayk/SourceFlow.Net/wiki)
+- [GitHub Repository](https://github.com/CodeShayk/SourceFlow.Net)
+- [Report Issues](https://github.com/CodeShayk/SourceFlow.Net/issues)
+- [Release Notes](https://github.com/CodeShayk/SourceFlow.Net/blob/master/CHANGELOG.md)
+
+## Support
+
+- **Issues**: [GitHub Issues](https://github.com/CodeShayk/SourceFlow.Net/issues/new/choose)
+- **Discussions**: [GitHub Discussions](https://github.com/CodeShayk/SourceFlow.Net/discussions)
+
+## License
+
+This project is licensed under the [MIT License](https://github.com/CodeShayk/SourceFlow.Net/blob/master/LICENSE).
diff --git a/docs/Stores.EntityFramework b/docs/Stores.EntityFramework
new file mode 100644
index 0000000..fddeaf4
--- /dev/null
+++ b/docs/Stores.EntityFramework
@@ -0,0 +1,388 @@
+# SourceFlow.Net.EntityFramework
+
+Entity Framework Core persistence provider for SourceFlow.Net. Provides implementations of `ICommandStore`, `IEntityStore`, and `IViewModelStore` using Entity Framework Core with full support for relational data models.
+
+## Features
+
+- **Entity Framework Core implementation** of SourceFlow stores
+- **Clean separation of concerns**: persistence layer handles only database operations
+- **CommandData DTO** for serialization separation from domain logic
+- **SQL Server by default**: Convenient methods for SQL Server with single or separate connection strings
+- **Database-agnostic support**: Use PostgreSQL, MySQL, SQLite, or any EF Core provider
+- **Flexible table naming conventions**: Configure casing, pluralization, prefixes, suffixes, and schemas
+- **Full async support** with proper Entity Framework tracking management
+- **Scoped service lifetimes** to prevent captive dependency issues
+- **Optimized change tracking** with `AsNoTracking()` and entity detachment
+- **Mix and match databases**: Use different databases for commands, entities, and view models
+
+### Production-Ready Enhancements
+
+- **🛡️ Resilience with Polly**: Retry policies, circuit breakers, and timeouts for fault tolerance
+- **📊 Observability with OpenTelemetry**: Distributed tracing, metrics, and performance monitoring
+- **⚡ Memory Optimization with ArrayPool**: Reduced GC pressure for high-throughput scenarios
+
+**[See ENHANCEMENTS.md for detailed configuration and usage →](ENHANCEMENTS.md)**
+
+## Architecture
+
+### Layered Design
+
+The implementation follows a clean layered architecture:
+
+1. **Store Layer** (`EfCommandStore`, `EfEntityStore`, `EfViewModelStore`)
+ - Handles only database persistence operations
+ - Works with data transfer objects (DTOs) for commands
+ - Uses Entity Framework Core for data access
+ - Manages change tracking and database connections
+
+2. **Adapter Layer** (`CommandStoreAdapter`, `EntityStoreAdapter`, `ViewModelStoreAdapter`)
+ - Handles serialization/deserialization of domain objects
+ - Converts between domain models and DTOs
+ - Lives in the core `SourceFlow` package
+
+3. **Service Lifetimes**
+ - All stores and adapters are registered as **Scoped** services
+ - Prevents captive dependency issues with DbContext
+ - Ensures proper disposal of database connections
+
+### CommandData DTO
+
+The `CommandData` class is a data transfer object used for command persistence:
+
+```csharp
+public class CommandData
+{
+ public int EntityId { get; set; }
+ public int SequenceNo { get; set; }
+ public string CommandName { get; set; }
+ public string CommandType { get; set; }
+ public string PayloadType { get; set; }
+ public string PayloadData { get; set; }
+ public string Metadata { get; set; }
+ public DateTime Timestamp { get; set; }
+}
+```
+
+This separation ensures:
+- `ICommandStore` interface works with serialized data only
+- Serialization logic lives in `CommandStoreAdapter`
+- Database layer is independent of domain serialization concerns
+- Better testability and maintainability
+
+### Change Tracking Optimization
+
+The stores use several techniques to optimize Entity Framework change tracking:
+
+- `AsNoTracking()` for read operations to improve performance
+- `EntityState.Detached` after save operations to prevent tracking conflicts
+- `ChangeTracker.Clear()` in command store to prevent caching issues
+- Ensures concurrent operations don't conflict with tracked entities
+
+## Installation
+
+```xml
+
+```
+
+## Usage
+
+SourceFlow.Net.EntityFramework provides two types of registration methods:
+
+1. **SQL Server Methods** - Convenient methods that use SQL Server by default (`AddSourceFlowEfStores`)
+2. **Database-Agnostic Methods** - Use any EF Core provider (`AddSourceFlowEfStoresWithCustomProvider`)
+
+### SQL Server (Default Provider)
+
+#### Single Connection String
+
+```csharp
+services.AddSourceFlowEfStores("Server=localhost;Database=SourceFlow;Trusted_Connection=true;");
+```
+
+#### Separate Connection Strings Per Store
+
+```csharp
+services.AddSourceFlowEfStores(
+ commandConnectionString: "Server=localhost;Database=SourceFlow.Commands;Trusted_Connection=true;",
+ entityConnectionString: "Server=localhost;Database=SourceFlow.Entities;Trusted_Connection=true;",
+ viewModelConnectionString: "Server=localhost;Database=SourceFlow.ViewModels;Trusted_Connection=true;"
+);
+```
+
+### Other Databases (Custom Provider)
+
+For PostgreSQL, MySQL, SQLite, or any other EF Core supported database:
+
+#### PostgreSQL
+
+```csharp
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseNpgsql("Host=localhost;Database=sourceflow;Username=postgres;Password=pass"));
+```
+
+#### MySQL
+
+```csharp
+var serverVersion = new MySqlServerVersion(new Version(8, 0, 21));
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseMySql("Server=localhost;Database=sourceflow;User=root;Password=pass", serverVersion));
+```
+
+#### SQLite
+
+```csharp
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseSqlite("Data Source=sourceflow.db"));
+```
+
+#### Different Databases Per Store
+
+You can even use different databases for each store:
+
+```csharp
+services.AddSourceFlowEfStoresWithCustomProviders(
+ commandContextConfig: opt => opt.UseNpgsql(postgresConnectionString),
+ entityContextConfig: opt => opt.UseSqlite(sqliteConnectionString),
+ viewModelContextConfig: opt => opt.UseSqlServer(sqlServerConnectionString)
+);
+```
+
+### Using Configuration (SQL Server)
+
+You can also configure connection strings using `IConfiguration`:
+
+```csharp
+// In appsettings.json:
+{
+ "ConnectionStrings": {
+ "SourceFlow.Command": "Server=localhost;Database=SourceFlow.Commands;Trusted_Connection=true;",
+ "SourceFlow.Entity": "Server=localhost;Database=SourceFlow.Entities;Trusted_Connection=true;",
+ "SourceFlow.ViewModel": "Server=localhost;Database=SourceFlow.ViewModels;Trusted_Connection=true;"
+ }
+}
+
+// In your startup code:
+services.AddSourceFlowEfStores(configuration);
+```
+
+### Options-Based Configuration
+
+For more complex scenarios with SQL Server, you can use the options pattern:
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.CommandConnectionString = GetCommandConnectionString();
+ options.EntityConnectionString = GetEntityConnectionString();
+ options.ViewModelConnectionString = GetViewModelConnectionString();
+});
+```
+
+## Connection String Resolution (SQL Server methods)
+
+The system follows this hierarchy for connection string resolution:
+
+1. If a specific connection string is configured for a store type, use it
+2. If no specific string exists, fall back to the default connection string
+3. If neither is available, throw an exception
+
+## Supported Databases
+
+This package works with any database that Entity Framework Core supports through the generic configuration methods, including:
+
+- SQL Server (via dedicated methods)
+- SQLite (via generic methods)
+- PostgreSQL (via generic methods)
+- MySQL (via generic methods)
+- Oracle (via generic methods)
+- In-memory databases (via generic methods)
+- And many others
+
+## Testing
+
+For testing scenarios, we recommend using SQLite in-memory databases:
+
+```csharp
+services.AddSourceFlowEfStoresWithCustomProvider(optionsBuilder =>
+ optionsBuilder.UseSqlite("DataSource=:memory:"));
+```
+
+Or for SQL Server testing:
+
+```csharp
+services.AddSourceFlowEfStores("DataSource=:memory:");
+```
+
+## Table Naming Conventions
+
+SourceFlow.Net.EntityFramework supports flexible table naming conventions to match your database standards.
+
+### Configuration
+
+Configure naming conventions using the `SourceFlowEfOptions`:
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Configure entity table naming
+ options.EntityTableNaming.Casing = TableNameCasing.SnakeCase;
+ options.EntityTableNaming.Pluralize = true;
+ options.EntityTableNaming.Prefix = "sf_";
+
+ // Configure view model table naming
+ options.ViewModelTableNaming.Casing = TableNameCasing.PascalCase;
+ options.ViewModelTableNaming.Suffix = "View";
+
+ // Configure command table naming
+ options.CommandTableNaming.Casing = TableNameCasing.LowerCase;
+ options.CommandTableNaming.UseSchema = true;
+ options.CommandTableNaming.SchemaName = "audit";
+});
+```
+
+### Naming Convention Options
+
+Each `TableNamingConvention` supports the following options:
+
+**Casing Styles:**
+- `PascalCase` - First letter capitalized (e.g., `BankAccount`)
+- `CamelCase` - First letter lowercase (e.g., `bankAccount`)
+- `SnakeCase` - Lowercase with underscores (e.g., `bank_account`)
+- `LowerCase` - All lowercase (e.g., `bankaccount`)
+- `UpperCase` - All uppercase (e.g., `BANKACCOUNT`)
+
+**Other Options:**
+- `Pluralize` - Pluralize table names (e.g., `BankAccount` → `BankAccounts`)
+- `Prefix` - Add a prefix to all table names (e.g., `"sf_"` → `sf_BankAccount`)
+- `Suffix` - Add a suffix to all table names (e.g., `"_tbl"` → `BankAccount_tbl`)
+- `UseSchema` - Whether to use a schema name
+- `SchemaName` - The schema name to use (when `UseSchema` is true)
+
+### Examples
+
+**Snake case with pluralization:**
+```csharp
+options.EntityTableNaming.Casing = TableNameCasing.SnakeCase;
+options.EntityTableNaming.Pluralize = true;
+// BankAccount → bank_accounts
+```
+
+**Prefix for all entity tables:**
+```csharp
+options.EntityTableNaming.Prefix = "Entity_";
+// BankAccount → Entity_BankAccount
+```
+
+**Schema-based organization:**
+```csharp
+options.CommandTableNaming.UseSchema = true;
+options.CommandTableNaming.SchemaName = "commands";
+// Commands table goes in commands.CommandRecord schema
+```
+
+**Combined conventions:**
+```csharp
+options.ViewModelTableNaming.Casing = TableNameCasing.SnakeCase;
+options.ViewModelTableNaming.Prefix = "vm_";
+options.ViewModelTableNaming.Pluralize = true;
+// AccountSummary → vm_account_summaries
+```
+
+### Default Behavior
+
+By default, all naming conventions use:
+- `Casing = TableNameCasing.PascalCase`
+- `Pluralize = false`
+- No prefix or suffix
+- No schema
+
+If you don't configure naming conventions, tables will be named using the entity/view model type names as-is.
+
+## Implementation Details
+
+### Command Serialization
+
+Commands are serialized using `System.Text.Json` with the following approach:
+
+- **Payload serialization**: Uses the concrete type of the payload, not the interface type
+- **Type information**: Stores `AssemblyQualifiedName` for both command and payload types
+- **Metadata**: Serialized separately to maintain sequence numbers and timestamps
+- **Deserialization**: Uses reflection to recreate command instances with parameterless constructors
+
+Example from `CommandStoreAdapter`:
+
+```csharp
+// Serialize using concrete type to capture all properties
+var payloadJson = command.Payload != null
+ ? System.Text.Json.JsonSerializer.Serialize(command.Payload, command.Payload.GetType())
+ : string.Empty;
+```
+
+### Entity Framework Tracking Management
+
+The stores implement careful tracking management to prevent common EF Core issues:
+
+**In EfCommandStore:**
+```csharp
+// Clear change tracker after save to prevent caching issues
+_context.Commands.Add(commandRecord);
+await _context.SaveChangesAsync();
+_context.ChangeTracker.Clear();
+```
+
+**In EfEntityStore and EfViewModelStore:**
+```csharp
+// Use AsNoTracking for existence checks
+var exists = await _context.Set()
+ .AsNoTracking()
+ .AnyAsync(e => e.Id == entity.Id);
+
+// Detach after save to prevent tracking conflicts
+await _context.SaveChangesAsync();
+_context.Entry(entity).State = EntityState.Detached;
+```
+
+### Service Registration
+
+All services are registered with **Scoped** lifetime:
+
+```csharp
+// Store adapters must be Scoped to match the lifetime of the underlying stores
+services.TryAddScoped();
+services.TryAddScoped();
+services.TryAddScoped();
+
+// Stores are also Scoped to work with DbContext
+services.AddScoped();
+services.AddScoped();
+services.AddScoped();
+```
+
+This prevents the captive dependency anti-pattern where singleton services capture scoped DbContext instances.
+
+## Best Practices
+
+1. **Always use Scoped services** - Don't register stores or adapters as Singleton
+2. **Enable database migrations** - Call `ApplyMigrations()` on EntityDbContext and ViewModelDbContext after `EnsureCreated()`
+3. **Handle deserialization failures** - The CommandStoreAdapter silently skips commands that can't be deserialized
+4. **Use parameterless constructors** - All command classes need a parameterless constructor for deserialization
+5. **Separate databases for testing** - Use fresh database instances for each test to avoid state conflicts
+6. **Configure proper connection pooling** - For production, ensure your connection strings include appropriate pooling settings
+
+## Troubleshooting
+
+### "Instance already being tracked" errors
+This occurs when Entity Framework tries to track multiple instances of the same entity. The stores now use `AsNoTracking()` and entity detachment to prevent this.
+
+### Commands not deserializing correctly
+Ensure your command classes have:
+- A public parameterless constructor
+- The payload is serialized using the concrete type, not the interface
+
+### Sequence number conflicts
+The `CommandStoreAdapter` calculates the next sequence number by loading all commands for an entity. Ensure concurrent operations are properly serialized at the application level if needed.
+
+### DbContext lifetime issues
+All stores and adapters must be registered as Scoped services. Singleton registration will cause DbContext lifetime issues and connection leaks.
diff --git a/docs/wiki.md b/docs/wiki.md
new file mode 100644
index 0000000..751d5a8
--- /dev/null
+++ b/docs/wiki.md
@@ -0,0 +1,2682 @@
+# SourceFlow.Net - Complete Guide
+
+## Table of Contents
+1. [Introduction](#introduction)
+2. [Core Concepts](#core-concepts)
+3. [Architecture Overview](#architecture-overview)
+4. [Getting Started](#getting-started)
+5. [Framework Components](#framework-components)
+6. [Persistence with Entity Framework](#persistence-with-entity-framework)
+7. [EntityFramework Usage Examples](#entityframework-usage-examples)
+8. [Implementation Guide](#implementation-guide)
+9. [Advanced Features](#advanced-features)
+10. [Performance and Observability](#performance-and-observability)
+11. [Best Practices](#best-practices)
+12. [FAQ](#faq)
+
+---
+
+## Introduction
+
+**SourceFlow.Net** is a modern, lightweight, and extensible .NET framework designed for building scalable event-sourced applications using Domain-Driven Design (DDD) principles and Command Query Responsibility Segregation (CQRS) patterns. Built for .NET 8+ with performance and developer experience as core priorities.
+
+### What Makes SourceFlow.Net Special?
+
+SourceFlow.Net provides a complete toolkit for event sourcing, domain modeling, and command/query separation, enabling developers to build maintainable, scalable applications with a strong foundation in proven architectural patterns.
+
+### Key Features
+
+* 🏗️ **Domain-Driven Design Support** - First-class support for aggregates, entities, value objects
+* ⚡ **CQRS Implementation** - Complete command/query separation with optimized read models
+* 📊 **Event Sourcing Foundation** - Event-first design with full audit trail
+* 🧱 **Clean Architecture** - Clear separation of concerns and dependency management
+* 💾 **Flexible Persistence** - Multiple storage options including Entity Framework Core
+* 🔄 **Event Replay** - Built-in command replay for debugging and state reconstruction
+* 🎯 **Type Safety** - Strongly-typed commands, events, and projections
+* 📦 **Dependency Injection** - Seamless integration with .NET DI container
+* 📈 **OpenTelemetry Integration** - Built-in distributed tracing and metrics for operations at scale
+* ⚡ **Memory Optimization** - ArrayPool-based optimization for extreme throughput scenarios
+* 🛡️ **Resilience Patterns** - Polly integration for fault tolerance with retry policies and circuit breakers
+
+---
+
+## Core Concepts
+
+### Event Sourcing
+
+**Event Sourcing** is an architectural pattern where the state of an application is determined by a sequence of events. Instead of storing the current state directly, the system stores all the events that have occurred, allowing for complete state reconstruction at any point in time.
+
+#### Key Benefits:
+- **Complete Audit Trail**: Every change is recorded as an immutable event
+- **Time Travel**: Reconstruct system state at any point in history
+- **Debugging**: Full visibility into how the system reached its current state
+- **Scalability**: Events can be replayed to build multiple read models
+
+#### Example in SourceFlow.Net:
+```csharp
+// Events are immutable records of what happened
+public class AccountCreated : Event
+{
+ public AccountCreated(BankAccount payload) : base(payload) { }
+}
+
+public class MoneyDeposited : Event
+{
+ public MoneyDeposited(BankAccount payload) : base(payload) { }
+}
+```
+
+### Domain-Driven Design (DDD)
+
+**Domain-Driven Design** is a software design approach that focuses on modeling software to match the business domain. It emphasizes collaboration between technical and domain experts to create a shared understanding of the problem space.
+
+#### Core DDD Elements in SourceFlow.Net:
+
+**Entities**: Objects with unique identity
+```csharp
+public class BankAccount : IEntity
+{
+ public int Id { get; set; }
+ public string AccountName { get; set; }
+ public decimal Balance { get; set; }
+ public bool IsClosed { get; set; }
+ public DateTime CreatedOn { get; set; }
+}
+```
+
+**Aggregates**: Coordinate business logic and ensure consistency
+```csharp
+public class AccountAggregate : Aggregate
+{
+ public void CreateAccount(int accountId, string holder, decimal amount)
+ {
+ // Business logic validation
+ Send(new CreateAccount(new CreateAccountPayload
+ {
+ Id = accountId,
+ AccountName = holder,
+ InitialAmount = amount
+ }));
+ }
+}
+```
+
+**Sagas**: Orchestrate long-running business processes
+```csharp
+public class AccountSaga : Saga, IHandles
+{
+ public async Task Handle(CreateAccount command)
+ {
+ // Validate, persist, and raise events
+ var account = new BankAccount { /* ... */ };
+ await repository.Persist(account);
+ await Raise(new AccountCreated(account));
+ }
+}
+```
+
+### Command Query Responsibility Segregation (CQRS)
+
+**CQRS** separates read and write operations, allowing for optimized data models for different purposes.
+
+#### Commands: Represent intent to change state
+```csharp
+public class CreateAccount : Command
+{
+ // Parameterless constructor required for deserialization
+ public CreateAccount() : base() { }
+
+ public CreateAccount(CreateAccountPayload payload) : base(payload) { }
+}
+```
+
+#### Queries: Handled through optimized view models
+```csharp
+public class AccountViewModel : IViewModel
+{
+ public int Id { get; set; }
+ public string AccountName { get; set; }
+ public decimal CurrentBalance { get; set; }
+ public DateTime LastUpdated { get; set; }
+ public int TransactionCount { get; set; }
+}
+```
+
+#### Projections: Update read models based on events
+```csharp
+public class AccountProjection : IProjectOn, IProjectOn
+{
+ public async Task Apply(AccountCreated @event)
+ {
+ var view = new AccountViewModel
+ {
+ Id = @event.Payload.Id,
+ AccountName = @event.Payload.AccountName,
+ CurrentBalance = @event.Payload.Balance
+ };
+ await provider.Push(view);
+ }
+}
+```
+
+---
+
+## Architecture Overview
+
+### High-Level Architecture
+
+
+
+### Component Interactions
+
+1. **Aggregates** encapsulate business logic and send commands
+2. **Command Bus** routes commands to appropriate saga handlers
+3. **Sagas** handle commands and maintain consistency across aggregates
+4. **Sagas** persist entities to the **Entity Store**
+5. **Sagas** raise events to the **Event Queue**
+6. **Event Queue** dispatches events to subscribers
+7. **Views** are projections that update read models (ViewModels) based on events
+8. **Command Store** persists commands for replay capability
+9. **Entity Store** persists root aggregates (entities) within bounded context
+10. **ViewModel Store** persists transformed view models from events
+
+**Entity Framework Stores** provide persistence using EF Core with support for multiple databases
+
+---
+
+## Getting Started
+
+### Installation
+
+```bash
+# Install the core package
+dotnet add package SourceFlow
+
+# Install Entity Framework persistence
+dotnet add package SourceFlow.Stores.EntityFramework
+```
+
+### Basic Setup
+
+```csharp
+// Program.cs
+using SourceFlow;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+var services = new ServiceCollection();
+
+// Add logging
+services.AddLogging(builder =>
+{
+ builder.AddConsole();
+ builder.SetMinimumLevel(LogLevel.Information);
+});
+
+// Register entity and view model types BEFORE building service provider
+EntityDbContext.RegisterAssembly(typeof(Program).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(Program).Assembly);
+
+// Configure SourceFlow with automatic discovery
+services.UseSourceFlow(typeof(Program).Assembly);
+
+// Add Entity Framework stores with SQL Server (default)
+services.AddSourceFlowEfStores(
+ "Server=localhost;Database=SourceFlow;Integrated Security=true;TrustServerCertificate=true;");
+
+var serviceProvider = services.BuildServiceProvider();
+
+// Initialize databases
+var commandContext = serviceProvider.GetRequiredService();
+await commandContext.Database.EnsureCreatedAsync();
+
+var entityContext = serviceProvider.GetRequiredService();
+await entityContext.Database.EnsureCreatedAsync();
+entityContext.ApplyMigrations();
+
+var viewModelContext = serviceProvider.GetRequiredService();
+await viewModelContext.Database.EnsureCreatedAsync();
+viewModelContext.ApplyMigrations();
+
+// Start using SourceFlow
+var aggregateFactory = serviceProvider.GetRequiredService();
+var accountAggregate = await aggregateFactory.Create();
+
+accountAggregate.CreateAccount(1, "John Doe", 1000m);
+```
+
+For other database providers (PostgreSQL, MySQL, SQLite), see [EntityFramework Usage Examples](#entityframework-usage-examples).
+
+---
+
+## Framework Components
+
+### 1. Aggregates
+
+Aggregates are the primary building blocks that encapsulate business logic and coordinate with the command bus.
+
+```csharp
+public abstract class Aggregate : IAggregate
+ where TEntity : class, IEntity
+{
+ protected ICommandPublisher commandPublisher;
+ protected ILogger logger;
+
+ // Send commands to command bus
+ protected async Task Send(ICommand command);
+
+ // Subscribe to external events
+ public virtual Task On(IEvent @event);
+}
+```
+
+**Key Features:**
+- Command publishing
+- Event subscription for external changes
+- Logger integration
+- Generic entity support
+
+### 2. Sagas
+
+Sagas handle commands and coordinate business processes, maintaining consistency across aggregate boundaries.
+
+```csharp
+public abstract class Saga : ISaga
+ where TEntity : class, IEntity
+{
+ protected IEntityStoreAdapter repository;
+ protected ICommandPublisher commandPublisher;
+ protected IEventQueue eventQueue;
+ protected ILogger logger;
+
+ // Publish commands
+ protected async Task Publish(TCommand command);
+
+ // Raise events
+ protected async Task Raise(TEvent @event);
+}
+```
+
+**Key Features:**
+- Dynamic command handling via `IHandles`
+- Event publishing
+- Repository access for persistence
+- Built-in logging
+
+### 3. Command Bus
+
+The command bus routes commands to appropriate saga handlers and manages command persistence.
+
+```csharp
+public interface ICommandBus
+{
+ // Publish commands to sagas
+ Task Publish(TCommand command) where TCommand : ICommand;
+
+ // Event dispatchers for command lifecycle
+ event EventHandler Dispatchers;
+}
+```
+
+### 4. Event Queue
+
+The event queue manages event flow and dispatches events to subscribers.
+
+```csharp
+public interface IEventQueue
+{
+ // Enqueue events for processing
+ Task Enqueue(TEvent @event) where TEvent : IEvent;
+
+ // Event dispatchers
+ event EventHandler Dispatchers;
+}
+```
+
+### 5. Stores (Persistence Layer)
+
+SourceFlow.Net defines three core store interfaces:
+
+#### ICommandStore
+Persists commands for event sourcing and replay
+```csharp
+public interface ICommandStore
+{
+ Task Save(ICommand command);
+ Task> Load(int entityId);
+}
+```
+
+#### IEntityStore
+Persists domain entities
+```csharp
+public interface IEntityStore
+{
+ Task Persist(TEntity entity) where TEntity : class, IEntity;
+ Task Get(int id) where TEntity : class, IEntity;
+ Task Delete(TEntity entity) where TEntity : class, IEntity;
+}
+```
+
+#### IViewModelStore
+Persists read models (projections)
+```csharp
+public interface IViewModelStore
+{
+ Task Persist(TViewModel model) where TViewModel : class, IViewModel;
+ Task Get(int id) where TViewModel : class, IViewModel;
+ Task Delete(TViewModel model) where TViewModel : class, IViewModel;
+}
+```
+
+---
+
+## Persistence with Entity Framework
+
+SourceFlow.Stores.EntityFramework provides production-ready persistence using Entity Framework Core with support for multiple database providers.
+
+### Features
+
+- ✅ **Multiple Database Support**: SQL Server, PostgreSQL, SQLite, and more
+- ✅ **Flexible Configuration**: Single or separate connection strings per store
+- ✅ **Dynamic Type Registration**: Runtime registration of entities and view models
+- ✅ **Migration Support**: Manual table creation bypassing EF Core model caching
+- ✅ **Thread-Safe**: Designed for concurrent access
+- ✅ **Optimized Tracking**: Proper EF Core change tracking management
+- ✅ **Production-Ready Enhancements**: Resilience, observability, and memory optimization
+
+### Installation
+
+```bash
+dotnet add package SourceFlow.Stores.EntityFramework
+```
+
+### Configuration Options
+
+#### 1. Single Connection String (All Stores)
+
+Use the same database for all stores:
+
+```csharp
+services.AddSourceFlowEfStores("Server=localhost;Database=SourceFlow;Integrated Security=true;");
+```
+
+#### 2. Separate Connection Strings
+
+Use different databases for each store:
+
+```csharp
+services.AddSourceFlowEfStores(
+ commandConnectionString: "Server=localhost;Database=SourceFlow_Commands;...",
+ entityConnectionString: "Server=localhost;Database=SourceFlow_Entities;...",
+ viewModelConnectionString: "Server=localhost;Database=SourceFlow_Views;..."
+);
+```
+
+#### 3. Configuration-Based
+
+Read from `appsettings.json`:
+
+```json
+{
+ "ConnectionStrings": {
+ "SourceFlow.Default": "Server=localhost;Database=SourceFlow;Integrated Security=true;",
+ "SourceFlow.Command": "Server=localhost;Database=Commands;...",
+ "SourceFlow.Entity": "Server=localhost;Database=Entities;...",
+ "SourceFlow.ViewModel": "Server=localhost;Database=Views;..."
+ }
+}
+```
+
+```csharp
+services.AddSourceFlowEfStores(configuration);
+```
+
+#### 4. Options Pattern
+
+Configure using options:
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = "Server=localhost;Database=SourceFlow;...";
+ // Or specify individual connection strings
+ options.CommandConnectionString = "...";
+ options.EntityConnectionString = "...";
+ options.ViewModelConnectionString = "...";
+});
+```
+
+#### 5. Custom Database Provider
+
+Use PostgreSQL, SQLite, or other providers:
+
+```csharp
+// PostgreSQL
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseNpgsql("Host=localhost;Database=sourceflow;Username=postgres;Password=..."));
+
+// SQLite
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseSqlite("Data Source=sourceflow.db"));
+
+// In-Memory (for testing)
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseInMemoryDatabase("SourceFlowTest"));
+```
+
+#### 6. Different Providers Per Store
+
+Use different database types for each store:
+
+```csharp
+services.AddSourceFlowEfStoresWithCustomProviders(
+ commandContextConfig: options => options.UseSqlServer("..."),
+ entityContextConfig: options => options.UseNpgsql("..."),
+ viewModelContextConfig: options => options.UseSqlite("...")
+);
+```
+
+### Dynamic Type Registration
+
+Entity Framework requires types to be registered before creating the database schema. SourceFlow.Stores.EntityFramework provides multiple registration strategies:
+
+#### 1. Explicit Type Registration
+
+Register specific types before database initialization:
+
+```csharp
+// In your startup or test setup
+EntityDbContext.RegisterEntityType();
+EntityDbContext.RegisterEntityType();
+
+ViewModelDbContext.RegisterViewModelType();
+ViewModelDbContext.RegisterViewModelType();
+
+// Then build service provider and ensure databases are created
+var serviceProvider = services.BuildServiceProvider();
+
+var entityContext = serviceProvider.GetRequiredService();
+entityContext.Database.EnsureCreated();
+entityContext.ApplyMigrations(); // Creates tables for registered types
+
+var viewModelContext = serviceProvider.GetRequiredService();
+viewModelContext.Database.EnsureCreated();
+viewModelContext.ApplyMigrations(); // Creates tables for registered view models
+```
+
+#### 2. Assembly Scanning
+
+Register all types from an assembly:
+
+```csharp
+// Register the test or application assembly
+EntityDbContext.RegisterAssembly(typeof(BankAccount).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(AccountViewModel).Assembly);
+
+var serviceProvider = services.BuildServiceProvider();
+
+// Apply migrations to create tables
+var entityContext = serviceProvider.GetRequiredService();
+entityContext.Database.EnsureCreated();
+entityContext.ApplyMigrations();
+
+var viewModelContext = serviceProvider.GetRequiredService();
+viewModelContext.Database.EnsureCreated();
+viewModelContext.ApplyMigrations();
+```
+
+#### 3. Auto-Discovery (Fallback)
+
+The DbContexts automatically discover types from loaded assemblies (fallback mechanism):
+
+```csharp
+// Just ensure databases are created
+var entityContext = serviceProvider.GetRequiredService();
+entityContext.Database.EnsureCreated();
+
+var viewModelContext = serviceProvider.GetRequiredService();
+viewModelContext.Database.EnsureCreated();
+
+// Note: This may not catch all types reliably; explicit registration is recommended
+```
+
+### Table Naming Convention
+
+All dynamically created tables use the `T` prefix:
+
+- `BankAccount` entity → `TBankAccount` table
+- `AccountViewModel` → `TAccountViewModel` table
+- `Customer` entity → `TCustomer` table
+
+This convention helps distinguish dynamically created tables from EF Core's built-in tables.
+
+### Migration Helper
+
+The `DbContextMigrationHelper` manually creates database schemas, bypassing EF Core's model caching:
+
+```csharp
+// Called automatically by ApplyMigrations()
+public static void CreateEntityTables(
+ EntityDbContext context,
+ IEnumerable entityTypes)
+{
+ // Creates tables with proper columns and primary keys
+ // Supports int, long, string, bool, DateTime, decimal, double, float, byte[], enums
+}
+
+public static void CreateViewModelTables(
+ ViewModelDbContext context,
+ IEnumerable viewModelTypes)
+{
+ // Creates tables for view models
+}
+```
+
+### Complete Setup Example
+
+```csharp
+using SourceFlow;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.EntityFrameworkCore;
+
+var services = new ServiceCollection();
+
+// Add logging
+services.AddLogging(builder =>
+{
+ builder.AddConsole();
+ builder.SetMinimumLevel(LogLevel.Information);
+});
+
+// Register types BEFORE building service provider
+EntityDbContext.RegisterAssembly(typeof(BankAccount).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(AccountViewModel).Assembly);
+
+// Configure SourceFlow
+services.UseSourceFlow(typeof(Program).Assembly);
+
+// Add Entity Framework stores (SQL Server by default)
+services.AddSourceFlowEfStores(
+ "Server=localhost;Database=SourceFlow;Integrated Security=true;TrustServerCertificate=true;");
+
+// Or use custom provider for other databases:
+// services.AddSourceFlowEfStoresWithCustomProvider(options =>
+// options.UseNpgsql("Host=localhost;Database=sourceflow;Username=postgres;Password=..."));
+
+var serviceProvider = services.BuildServiceProvider();
+
+// Ensure all databases are created and migrated
+var commandContext = serviceProvider.GetRequiredService();
+await commandContext.Database.EnsureCreatedAsync();
+
+var entityContext = serviceProvider.GetRequiredService();
+await entityContext.Database.EnsureCreatedAsync();
+entityContext.ApplyMigrations(); // Create tables for registered entity types
+
+var viewModelContext = serviceProvider.GetRequiredService();
+await viewModelContext.Database.EnsureCreatedAsync();
+viewModelContext.ApplyMigrations(); // Create tables for registered view model types
+
+// Start using SourceFlow
+var aggregateFactory = serviceProvider.GetRequiredService();
+var accountAggregate = await aggregateFactory.Create();
+
+accountAggregate.CreateAccount(1, "John Doe", 1000m);
+```
+
+### Testing with In-Memory Database
+
+For unit and integration tests, use SQLite in-memory databases with proper setup:
+
+```csharp
+using NUnit.Framework;
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+using SourceFlow.Stores.EntityFramework.Options;
+using SourceFlow.Stores.EntityFramework.Services;
+using SourceFlow.Stores.EntityFramework.Stores;
+
+[TestFixture]
+public class BankAccountIntegrationTests
+{
+ private ServiceProvider? _serviceProvider;
+ private SqliteConnection? _connection;
+
+ [SetUp]
+ public void Setup()
+ {
+ // Clear previous registrations
+ EntityDbContext.ClearRegistrations();
+ ViewModelDbContext.ClearRegistrations();
+
+ // Register test types
+ EntityDbContext.RegisterEntityType();
+ ViewModelDbContext.RegisterViewModelType();
+
+ // Create shared in-memory SQLite connection for all contexts
+ _connection = new SqliteConnection("DataSource=:memory:");
+ _connection.Open();
+
+ var services = new ServiceCollection();
+
+ // Add logging for better test diagnostics
+ services.AddLogging(builder =>
+ {
+ builder.AddConsole();
+ builder.SetMinimumLevel(LogLevel.Debug);
+ });
+
+ // Configure SQLite with shared connection
+ // Use EnableServiceProviderCaching(false) to avoid EF Core 9.0 multiple provider conflicts
+ services.AddDbContext(options =>
+ options.UseSqlite(_connection)
+ .EnableServiceProviderCaching(false));
+ services.AddDbContext(options =>
+ options.UseSqlite(_connection)
+ .EnableServiceProviderCaching(false));
+ services.AddDbContext(options =>
+ options.UseSqlite(_connection)
+ .EnableServiceProviderCaching(false));
+
+ // Register SourceFlowEfOptions with default settings
+ var efOptions = new SourceFlowEfOptions();
+ services.AddSingleton(efOptions);
+
+ // Register common services manually (avoids provider conflicts)
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+ services.AddScoped();
+
+ // Register SourceFlow
+ services.UseSourceFlow(Assembly.GetExecutingAssembly());
+
+ _serviceProvider = services.BuildServiceProvider();
+
+ // Create all database schemas
+ var commandContext = _serviceProvider.GetRequiredService();
+ commandContext.Database.EnsureCreated();
+
+ var entityContext = _serviceProvider.GetRequiredService();
+ entityContext.Database.EnsureCreated();
+ entityContext.ApplyMigrations(); // Create tables for registered entity types
+
+ var viewModelContext = _serviceProvider.GetRequiredService();
+ viewModelContext.Database.EnsureCreated();
+ viewModelContext.ApplyMigrations(); // Create tables for registered view model types
+ }
+
+ [TearDown]
+ public void TearDown()
+ {
+ // Clean up resources
+ _connection?.Close();
+ _connection?.Dispose();
+ _serviceProvider?.Dispose();
+ }
+
+ [Test]
+ public async Task CreateAccount_StoresInDatabase()
+ {
+ // Arrange
+ var aggregateFactory = _serviceProvider.GetRequiredService();
+ var accountAggregate = await aggregateFactory.Create();
+
+ // Act
+ accountAggregate.CreateAccount(1, "John Doe", 1000m);
+
+ // Wait for async processing
+ await Task.Delay(100);
+
+ // Assert
+ var entityStore = _serviceProvider.GetRequiredService();
+ var account = await entityStore.Get(1);
+
+ Assert.That(account, Is.Not.Null);
+ Assert.That(account.AccountName, Is.EqualTo("John Doe"));
+ Assert.That(account.Balance, Is.EqualTo(1000m));
+ }
+}
+```
+
+---
+
+## EntityFramework Usage Examples
+
+This section provides practical examples for common scenarios using SourceFlow.Stores.EntityFramework.
+
+### Example 1: Simple Console Application with SQL Server
+
+Complete working example for a console application:
+
+```csharp
+using System;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using SourceFlow;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+
+class Program
+{
+ static async Task Main(string[] args)
+ {
+ // Setup service collection
+ var services = new ServiceCollection();
+
+ // Add logging
+ services.AddLogging(builder =>
+ {
+ builder.AddConsole();
+ builder.SetMinimumLevel(LogLevel.Information);
+ });
+
+ // Register entity and view model types BEFORE building service provider
+ EntityDbContext.RegisterEntityType();
+ ViewModelDbContext.RegisterViewModelType();
+
+ // Configure SourceFlow
+ services.UseSourceFlow(typeof(Program).Assembly);
+
+ // Add Entity Framework stores with SQL Server
+ services.AddSourceFlowEfStores(
+ "Server=localhost;Database=SourceFlowDemo;Integrated Security=true;TrustServerCertificate=true;");
+
+ var serviceProvider = services.BuildServiceProvider();
+
+ // Ensure databases are created
+ var commandContext = serviceProvider.GetRequiredService();
+ await commandContext.Database.EnsureCreatedAsync();
+
+ var entityContext = serviceProvider.GetRequiredService();
+ await entityContext.Database.EnsureCreatedAsync();
+ entityContext.ApplyMigrations();
+
+ var viewModelContext = serviceProvider.GetRequiredService();
+ await viewModelContext.Database.EnsureCreatedAsync();
+ viewModelContext.ApplyMigrations();
+
+ // Use the aggregate
+ var aggregateFactory = serviceProvider.GetRequiredService();
+ var accountAggregate = await aggregateFactory.Create();
+
+ // Execute business operations
+ accountAggregate.CreateAccount(1, "Alice Smith", 5000m);
+ accountAggregate.Deposit(1, 1500m);
+ accountAggregate.Withdraw(1, 500m);
+
+ // Give async processing time to complete
+ await Task.Delay(500);
+
+ // Query the read model
+ var viewModelStore = serviceProvider.GetRequiredService();
+ var accountView = await viewModelStore.Find(1);
+
+ Console.WriteLine($"Account: {accountView.AccountName}");
+ Console.WriteLine($"Balance: {accountView.CurrentBalance:C}");
+ Console.WriteLine($"Transactions: {accountView.TransactionCount}");
+ Console.WriteLine($"Created: {accountView.CreatedDate:yyyy-MM-dd}");
+ }
+}
+```
+
+### Example 2: ASP.NET Core Web API with PostgreSQL
+
+Complete setup for a web API using PostgreSQL:
+
+```csharp
+// Program.cs
+using Microsoft.EntityFrameworkCore;
+using SourceFlow;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Add services to the container
+builder.Services.AddControllers();
+builder.Services.AddEndpointsApiExplorer();
+builder.Services.AddSwaggerGen();
+
+// Register entity and view model types
+EntityDbContext.RegisterAssembly(typeof(Program).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(Program).Assembly);
+
+// Configure SourceFlow with PostgreSQL
+builder.Services.UseSourceFlow(typeof(Program).Assembly);
+
+builder.Services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseNpgsql(builder.Configuration.GetConnectionString("SourceFlow")));
+
+var app = builder.Build();
+
+// Initialize databases on startup
+using (var scope = app.Services.CreateScope())
+{
+ var commandContext = scope.ServiceProvider.GetRequiredService();
+ await commandContext.Database.EnsureCreatedAsync();
+
+ var entityContext = scope.ServiceProvider.GetRequiredService();
+ await entityContext.Database.EnsureCreatedAsync();
+ entityContext.ApplyMigrations();
+
+ var viewModelContext = scope.ServiceProvider.GetRequiredService();
+ await viewModelContext.Database.EnsureCreatedAsync();
+ viewModelContext.ApplyMigrations();
+}
+
+if (app.Environment.IsDevelopment())
+{
+ app.UseSwagger();
+ app.UseSwaggerUI();
+}
+
+app.UseHttpsRedirection();
+app.UseAuthorization();
+app.MapControllers();
+app.Run();
+```
+
+```csharp
+// appsettings.json
+{
+ "ConnectionStrings": {
+ "SourceFlow": "Host=localhost;Database=sourceflow;Username=postgres;Password=yourpassword"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.EntityFrameworkCore": "Warning"
+ }
+ }
+}
+```
+
+```csharp
+// Controllers/AccountController.cs
+using Microsoft.AspNetCore.Mvc;
+using SourceFlow;
+
+[ApiController]
+[Route("api/[controller]")]
+public class AccountController : ControllerBase
+{
+ private readonly IAggregateFactory _aggregateFactory;
+ private readonly IViewModelStoreAdapter _viewModelStore;
+ private readonly ILogger _logger;
+
+ public AccountController(
+ IAggregateFactory aggregateFactory,
+ IViewModelStoreAdapter viewModelStore,
+ ILogger logger)
+ {
+ _aggregateFactory = aggregateFactory;
+ _viewModelStore = viewModelStore;
+ _logger = logger;
+ }
+
+ [HttpPost]
+ public async Task CreateAccount(CreateAccountRequest request)
+ {
+ var aggregate = await _aggregateFactory.Create();
+ aggregate.CreateAccount(request.Id, request.AccountName, request.InitialBalance);
+
+ _logger.LogInformation("Account created: {AccountId}", request.Id);
+ return CreatedAtAction(nameof(GetAccount), new { id = request.Id }, request);
+ }
+
+ [HttpGet("{id}")]
+ public async Task> GetAccount(int id)
+ {
+ try
+ {
+ var account = await _viewModelStore.Find(id);
+ return Ok(account);
+ }
+ catch (InvalidOperationException)
+ {
+ return NotFound();
+ }
+ }
+
+ [HttpPost("{id}/deposit")]
+ public async Task Deposit(int id, [FromBody] TransactionRequest request)
+ {
+ var aggregate = await _aggregateFactory.Create();
+ aggregate.Deposit(id, request.Amount);
+
+ _logger.LogInformation("Deposited {Amount} to account {AccountId}", request.Amount, id);
+ return NoContent();
+ }
+
+ [HttpPost("{id}/withdraw")]
+ public async Task Withdraw(int id, [FromBody] TransactionRequest request)
+ {
+ try
+ {
+ var aggregate = await _aggregateFactory.Create();
+ aggregate.Withdraw(id, request.Amount);
+
+ _logger.LogInformation("Withdrew {Amount} from account {AccountId}", request.Amount, id);
+ return NoContent();
+ }
+ catch (InvalidOperationException ex)
+ {
+ return BadRequest(new { error = ex.Message });
+ }
+ }
+}
+
+public record CreateAccountRequest(int Id, string AccountName, decimal InitialBalance);
+public record TransactionRequest(decimal Amount);
+```
+
+### Example 3: Microservices with Separate Databases
+
+Using different databases for different stores in a microservices architecture:
+
+```csharp
+// Program.cs for Banking Microservice
+var builder = WebApplication.CreateBuilder(args);
+
+// Register types
+EntityDbContext.RegisterAssembly(typeof(Program).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(Program).Assembly);
+
+// Configure SourceFlow
+builder.Services.UseSourceFlow(typeof(Program).Assembly);
+
+// Each store uses a different database optimized for its purpose
+builder.Services.AddSourceFlowEfStoresWithCustomProviders(
+ // Commands: PostgreSQL with JSONB support for efficient command storage
+ commandContextConfig: opt => opt.UseNpgsql(
+ builder.Configuration.GetConnectionString("CommandStore")),
+
+ // Entities: SQL Server with optimized indexes for transactional workload
+ entityContextConfig: opt => opt.UseSqlServer(
+ builder.Configuration.GetConnectionString("EntityStore")),
+
+ // ViewModels: SQLite for fast read queries in read-heavy scenarios
+ viewModelContextConfig: opt => opt.UseSqlite(
+ builder.Configuration.GetConnectionString("ViewStore"))
+);
+
+var app = builder.Build();
+
+// Initialize all databases
+using (var scope = app.Services.CreateScope())
+{
+ var commandContext = scope.ServiceProvider.GetRequiredService();
+ await commandContext.Database.MigrateAsync();
+
+ var entityContext = scope.ServiceProvider.GetRequiredService();
+ await entityContext.Database.MigrateAsync();
+ entityContext.ApplyMigrations();
+
+ var viewModelContext = scope.ServiceProvider.GetRequiredService();
+ await viewModelContext.Database.MigrateAsync();
+ viewModelContext.ApplyMigrations();
+}
+
+app.Run();
+```
+
+```json
+// appsettings.json
+{
+ "ConnectionStrings": {
+ "CommandStore": "Host=postgres-commands.internal;Database=banking_commands;Username=app;Password=...",
+ "EntityStore": "Server=sqlserver-entities.internal;Database=banking_entities;User Id=app;Password=...;",
+ "ViewStore": "Data Source=/data/banking_views.db"
+ }
+}
+```
+
+### Example 4: Production Configuration with Resilience and Observability
+
+Complete production setup with all enterprise features:
+
+```csharp
+// Program.cs
+using SourceFlow;
+using SourceFlow.Observability;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+using OpenTelemetry;
+using Microsoft.EntityFrameworkCore;
+
+var builder = WebApplication.CreateBuilder(args);
+
+// Register domain types
+EntityDbContext.RegisterAssembly(typeof(Program).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(Program).Assembly);
+
+// Configure SourceFlow with observability
+builder.Services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "BankingService";
+ options.ServiceVersion = builder.Configuration["AppVersion"] ?? "1.0.0";
+});
+
+// Configure OpenTelemetry exporters
+builder.Services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter(builder.Configuration["Observability:OtlpEndpoint"])
+ .AddSourceFlowResourceAttributes(
+ ("environment", builder.Environment.EnvironmentName),
+ ("deployment.region", builder.Configuration["Deployment:Region"]),
+ ("service.instance.id", Environment.MachineName)
+ )
+ .ConfigureSourceFlowBatchProcessing(
+ maxQueueSize: 2048,
+ maxExportBatchSize: 512,
+ scheduledDelayMilliseconds: 5000
+ );
+
+// Register SourceFlow
+builder.Services.UseSourceFlow(typeof(Program).Assembly);
+
+// Configure Entity Framework stores with resilience and observability
+builder.Services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = builder.Configuration.GetConnectionString("SourceFlow");
+
+ // Resilience configuration
+ options.Resilience.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 3;
+ options.Resilience.Retry.BaseDelayMs = 1000;
+ options.Resilience.Retry.UseExponentialBackoff = true;
+ options.Resilience.Retry.UseJitter = true;
+ options.Resilience.CircuitBreaker.Enabled = true;
+ options.Resilience.CircuitBreaker.FailureThreshold = 10;
+ options.Resilience.CircuitBreaker.BreakDurationMs = 60000;
+ options.Resilience.Timeout.Enabled = true;
+ options.Resilience.Timeout.TimeoutMs = 30000;
+
+ // Observability configuration
+ options.Observability.Enabled = true;
+ options.Observability.ServiceName = "BankingService.EntityFramework";
+ options.Observability.Tracing.Enabled = true;
+ options.Observability.Tracing.TraceDatabaseOperations = true;
+ options.Observability.Tracing.IncludeSqlInTraces = false; // Don't log SQL in production
+ options.Observability.Tracing.SamplingRatio = 0.1; // Sample 10%
+ options.Observability.Metrics.Enabled = true;
+ options.Observability.Metrics.CollectDatabaseMetrics = true;
+
+ // Table naming conventions
+ options.EntityTableNaming.Casing = TableNameCasing.SnakeCase;
+ options.EntityTableNaming.Pluralize = true;
+ options.ViewModelTableNaming.Casing = TableNameCasing.SnakeCase;
+ options.ViewModelTableNaming.Suffix = "_view";
+});
+
+// Add health checks
+builder.Services.AddHealthChecks()
+ .AddDbContextCheck("command-store")
+ .AddDbContextCheck("entity-store")
+ .AddDbContextCheck("viewmodel-store");
+
+var app = builder.Build();
+
+// Initialize databases
+using (var scope = app.Services.CreateScope())
+{
+ var logger = scope.ServiceProvider.GetRequiredService>();
+
+ try
+ {
+ var commandContext = scope.ServiceProvider.GetRequiredService();
+ await commandContext.Database.EnsureCreatedAsync();
+ logger.LogInformation("Command store initialized");
+
+ var entityContext = scope.ServiceProvider.GetRequiredService();
+ await entityContext.Database.EnsureCreatedAsync();
+ entityContext.ApplyMigrations();
+ logger.LogInformation("Entity store initialized");
+
+ var viewModelContext = scope.ServiceProvider.GetRequiredService();
+ await viewModelContext.Database.EnsureCreatedAsync();
+ viewModelContext.ApplyMigrations();
+ logger.LogInformation("ViewModel store initialized");
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Failed to initialize databases");
+ throw;
+ }
+}
+
+app.MapHealthChecks("/health");
+app.Run();
+```
+
+```json
+// appsettings.Production.json
+{
+ "ConnectionStrings": {
+ "SourceFlow": "Server=prod-db.internal;Database=BankingService;User Id=app_user;Password=...;Max Pool Size=100;Min Pool Size=10;"
+ },
+ "Observability": {
+ "OtlpEndpoint": "http://otel-collector.internal:4317"
+ },
+ "Deployment": {
+ "Region": "us-east-1"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "Microsoft.EntityFrameworkCore": "Warning",
+ "SourceFlow": "Information"
+ }
+ }
+}
+```
+
+### Example 5: Custom Table Naming with Schema Organization
+
+Organizing tables with custom naming conventions and schemas:
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Command store: Audit schema with snake_case
+ options.CommandTableNaming.UseSchema = true;
+ options.CommandTableNaming.SchemaName = "audit";
+ options.CommandTableNaming.Casing = TableNameCasing.SnakeCase;
+ // Results in: audit.command_record
+
+ // Entity store: Domain schema with pluralized tables
+ options.EntityTableNaming.UseSchema = true;
+ options.EntityTableNaming.SchemaName = "domain";
+ options.EntityTableNaming.Casing = TableNameCasing.SnakeCase;
+ options.EntityTableNaming.Pluralize = true;
+ // BankAccount -> domain.bank_accounts
+
+ // ViewModel store: Reporting schema with view suffix
+ options.ViewModelTableNaming.UseSchema = true;
+ options.ViewModelTableNaming.SchemaName = "reporting";
+ options.ViewModelTableNaming.Casing = TableNameCasing.SnakeCase;
+ options.ViewModelTableNaming.Suffix = "_view";
+ options.ViewModelTableNaming.Pluralize = true;
+ // AccountViewModel -> reporting.account_views
+});
+
+// Create schemas before initializing
+var entityContext = serviceProvider.GetRequiredService();
+await entityContext.Database.ExecuteSqlRawAsync("CREATE SCHEMA IF NOT EXISTS audit");
+await entityContext.Database.ExecuteSqlRawAsync("CREATE SCHEMA IF NOT EXISTS domain");
+await entityContext.Database.ExecuteSqlRawAsync("CREATE SCHEMA IF NOT EXISTS reporting");
+await entityContext.Database.EnsureCreatedAsync();
+entityContext.ApplyMigrations();
+```
+
+### Example 6: Background Service Processing Commands
+
+Processing commands in a background service:
+
+```csharp
+public class CommandProcessorBackgroundService : BackgroundService
+{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ILogger _logger;
+
+ public CommandProcessorBackgroundService(
+ IServiceProvider serviceProvider,
+ ILogger logger)
+ {
+ _serviceProvider = serviceProvider;
+ _logger = logger;
+ }
+
+ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
+ {
+ _logger.LogInformation("Command Processor Background Service starting");
+
+ while (!stoppingToken.IsCancellationRequested)
+ {
+ try
+ {
+ using var scope = _serviceProvider.CreateScope();
+
+ var commandStore = scope.ServiceProvider.GetRequiredService();
+ var aggregateFactory = scope.ServiceProvider.GetRequiredService();
+
+ // Process any pending commands (example: replay for specific entities)
+ var entityIds = await GetPendingEntityIds();
+
+ foreach (var entityId in entityIds)
+ {
+ var commands = await commandStore.Retrieve(entityId);
+
+ if (commands.Any())
+ {
+ _logger.LogInformation(
+ "Processing {Count} commands for entity {EntityId}",
+ commands.Count(), entityId);
+
+ // Replay commands to rebuild state
+ var aggregate = await aggregateFactory.Create();
+ // Process commands...
+ }
+ }
+
+ await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error in command processor");
+ await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
+ }
+ }
+
+ _logger.LogInformation("Command Processor Background Service stopping");
+ }
+
+ private async Task> GetPendingEntityIds()
+ {
+ // Implementation to identify entities needing processing
+ return new List();
+ }
+}
+
+// Register in Program.cs
+builder.Services.AddHostedService();
+```
+
+### Example 7: Multi-Tenant Setup with Database Per Tenant
+
+Implementing multi-tenancy with separate databases:
+
+```csharp
+// ITenantProvider.cs
+public interface ITenantProvider
+{
+ string GetCurrentTenantId();
+ string GetConnectionString(string tenantId);
+}
+
+// TenantDbContextFactory.cs
+public class TenantDbContextFactory where TContext : DbContext
+{
+ private readonly ITenantProvider _tenantProvider;
+ private readonly IServiceProvider _serviceProvider;
+
+ public TenantDbContextFactory(ITenantProvider tenantProvider, IServiceProvider serviceProvider)
+ {
+ _tenantProvider = tenantProvider;
+ _serviceProvider = serviceProvider;
+ }
+
+ public TContext CreateDbContext()
+ {
+ var tenantId = _tenantProvider.GetCurrentTenantId();
+ var connectionString = _tenantProvider.GetConnectionString(tenantId);
+
+ var optionsBuilder = new DbContextOptionsBuilder();
+ optionsBuilder.UseSqlServer(connectionString);
+
+ return (TContext)Activator.CreateInstance(typeof(TContext), optionsBuilder.Options);
+ }
+}
+
+// Program.cs
+builder.Services.AddScoped();
+
+// Register SourceFlow with multi-tenant support
+builder.Services.UseSourceFlow(typeof(Program).Assembly);
+
+// Custom multi-tenant store registration
+builder.Services.AddScoped(sp =>
+{
+ var tenantProvider = sp.GetRequiredService();
+ var tenantId = tenantProvider.GetCurrentTenantId();
+ var connectionString = tenantProvider.GetConnectionString(tenantId);
+
+ var optionsBuilder = new DbContextOptionsBuilder();
+ optionsBuilder.UseSqlServer(connectionString);
+
+ return new EntityDbContext(optionsBuilder.Options);
+});
+
+// Similar for CommandDbContext and ViewModelDbContext
+```
+
+---
+
+## Implementation Guide
+
+### Creating a Complete Feature
+
+Let's implement a complete banking feature using SourceFlow.Net with Entity Framework persistence:
+
+#### 1. Define Domain Objects
+
+```csharp
+// Entity
+public class BankAccount : IEntity
+{
+ public int Id { get; set; }
+ public string AccountName { get; set; }
+ public decimal Balance { get; set; }
+ public bool IsClosed { get; set; }
+ public DateTime CreatedOn { get; set; }
+ public DateTime ActiveOn { get; set; }
+ public string ClosureReason { get; set; }
+}
+
+// Command Payloads
+public class CreateAccountPayload : IPayload
+{
+ public int Id { get; set; }
+ public string AccountName { get; set; }
+ public decimal InitialAmount { get; set; }
+}
+
+public class TransactionPayload : IPayload
+{
+ public int Id { get; set; }
+ public decimal Amount { get; set; }
+}
+```
+
+#### 2. Create Commands
+
+```csharp
+public class CreateAccount : Command
+{
+ // Parameterless constructor required for command deserialization from store
+ public CreateAccount() : base() { }
+
+ public CreateAccount(CreateAccountPayload payload) : base(payload) { }
+}
+
+public class DepositMoney : Command
+{
+ // Parameterless constructor required for command deserialization from store
+ public DepositMoney() : base() { }
+
+ public DepositMoney(TransactionPayload payload) : base(payload) { }
+}
+
+public class WithdrawMoney : Command
+{
+ // Parameterless constructor required for command deserialization from store
+ public WithdrawMoney() : base() { }
+
+ public WithdrawMoney(TransactionPayload payload) : base(payload) { }
+}
+```
+
+#### 3. Define Events
+
+```csharp
+public class AccountCreated : Event
+{
+ public AccountCreated(BankAccount payload) : base(payload) { }
+}
+
+public class MoneyDeposited : Event
+{
+ public MoneyDeposited(BankAccount payload) : base(payload) { }
+}
+
+public class MoneyWithdrawn : Event
+{
+ public MoneyWithdrawn(BankAccount payload) : base(payload) { }
+}
+```
+
+#### 4. Implement Saga
+
+```csharp
+public class AccountSaga : Saga,
+ IHandles,
+ IHandles,
+ IHandles
+{
+ public async Task Handle(CreateAccount command)
+ {
+ // Validation
+ if (string.IsNullOrEmpty(command.Payload.AccountName))
+ throw new ArgumentException("Account name is required");
+
+ if (command.Payload.InitialAmount <= 0)
+ throw new ArgumentException("Initial amount must be positive");
+
+ // Create entity
+ var account = new BankAccount
+ {
+ Id = command.Payload.Id,
+ AccountName = command.Payload.AccountName,
+ Balance = command.Payload.InitialAmount,
+ CreatedOn = DateTime.UtcNow,
+ ActiveOn = DateTime.UtcNow
+ };
+
+ // Persist to Entity Store
+ await repository.Persist(account);
+
+ // Raise event
+ await Raise(new AccountCreated(account));
+
+ logger.LogInformation("Account created: {AccountId} for {Holder} with balance {Balance}",
+ account.Id, account.AccountName, account.Balance);
+ }
+
+ public async Task Handle(DepositMoney command)
+ {
+ var account = await repository.Get(command.Payload.Id);
+
+ if (account.IsClosed)
+ throw new InvalidOperationException("Cannot deposit to closed account");
+
+ account.Balance += command.Payload.Amount;
+ await repository.Persist(account);
+ await Raise(new MoneyDeposited(account));
+
+ logger.LogInformation("Deposited {Amount} to account {AccountId}. New balance: {Balance}",
+ command.Payload.Amount, account.Id, account.Balance);
+ }
+
+ public async Task Handle(WithdrawMoney command)
+ {
+ var account = await repository.Get(command.Payload.Id);
+
+ if (account.IsClosed)
+ throw new InvalidOperationException("Cannot withdraw from closed account");
+
+ if (account.Balance < command.Payload.Amount)
+ throw new InvalidOperationException("Insufficient funds");
+
+ account.Balance -= command.Payload.Amount;
+ await repository.Persist(account);
+ await Raise(new MoneyWithdrawn(account));
+
+ logger.LogInformation("Withdrew {Amount} from account {AccountId}. New balance: {Balance}",
+ command.Payload.Amount, account.Id, account.Balance);
+ }
+}
+```
+
+#### 5. Create Aggregate
+
+```csharp
+public interface IAccountAggregate : IAggregate
+{
+ void CreateAccount(int accountId, string holder, decimal amount);
+ void Deposit(int accountId, decimal amount);
+ void Withdraw(int accountId, decimal amount);
+}
+
+public class AccountAggregate : Aggregate, IAccountAggregate
+{
+ public void CreateAccount(int accountId, string holder, decimal amount)
+ {
+ Send(new CreateAccount(new CreateAccountPayload
+ {
+ Id = accountId,
+ AccountName = holder,
+ InitialAmount = amount
+ }));
+ }
+
+ public void Deposit(int accountId, decimal amount)
+ {
+ Send(new DepositMoney(new TransactionPayload
+ {
+ Id = accountId,
+ Amount = amount
+ }));
+ }
+
+ public void Withdraw(int accountId, decimal amount)
+ {
+ Send(new WithdrawMoney(new TransactionPayload
+ {
+ Id = accountId,
+ Amount = amount
+ }));
+ }
+}
+```
+
+#### 6. Build Read Models
+
+```csharp
+public class AccountViewModel : IViewModel
+{
+ public int Id { get; set; }
+ public string AccountName { get; set; }
+ public decimal CurrentBalance { get; set; }
+ public DateTime CreatedDate { get; set; }
+ public DateTime LastUpdated { get; set; }
+ public int TransactionCount { get; set; }
+ public bool IsClosed { get; set; }
+ public string ClosureReason { get; set; }
+ public int Version { get; set; }
+ public DateTime ActiveOn { get; set; }
+}
+
+public class AccountView : View,
+ IProjectOn,
+ IProjectOn,
+ IProjectOn
+{
+ public async Task Apply(AccountCreated @event)
+ {
+ var view = new AccountViewModel
+ {
+ Id = @event.Payload.Id,
+ AccountName = @event.Payload.AccountName,
+ CurrentBalance = @event.Payload.Balance,
+ CreatedDate = @event.Payload.CreatedOn,
+ LastUpdated = DateTime.UtcNow,
+ TransactionCount = 0,
+ IsClosed = false,
+ ActiveOn = @event.Payload.ActiveOn
+ };
+
+ await provider.Push(view);
+
+ logger.LogInformation("Created view model for account {AccountId}", view.Id);
+ }
+
+ public async Task Apply(MoneyDeposited @event)
+ {
+ var view = await provider.Find(@event.Payload.Id);
+ view.CurrentBalance = @event.Payload.Balance;
+ view.TransactionCount++;
+ view.LastUpdated = DateTime.UtcNow;
+
+ await provider.Push(view);
+
+ logger.LogInformation("Updated view model for account {AccountId} after deposit", view.Id);
+ }
+
+ public async Task Apply(MoneyWithdrawn @event)
+ {
+ var view = await provider.Find(@event.Payload.Id);
+ view.CurrentBalance = @event.Payload.Balance;
+ view.TransactionCount++;
+ view.LastUpdated = DateTime.UtcNow;
+
+ await provider.Push(view);
+
+ logger.LogInformation("Updated view model for account {AccountId} after withdrawal", view.Id);
+ }
+}
+```
+
+#### 7. Application Setup
+
+```csharp
+// Program.cs
+using SourceFlow;
+using SourceFlow.Stores.EntityFramework;
+using SourceFlow.Stores.EntityFramework.Extensions;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+var services = new ServiceCollection();
+
+// Add logging
+services.AddLogging(builder =>
+{
+ builder.AddConsole();
+ builder.SetMinimumLevel(LogLevel.Information);
+});
+
+// Register entity and view model types BEFORE building service provider
+EntityDbContext.RegisterEntityType();
+ViewModelDbContext.RegisterViewModelType();
+
+// Configure SourceFlow
+services.UseSourceFlow(typeof(Program).Assembly);
+
+// Add Entity Framework stores
+services.AddSourceFlowEfStores(
+ "Server=localhost;Database=SourceFlow;Integrated Security=true;TrustServerCertificate=true;");
+
+var serviceProvider = services.BuildServiceProvider();
+
+// Ensure databases are created and migrated
+var commandContext = serviceProvider.GetRequiredService();
+await commandContext.Database.EnsureCreatedAsync();
+
+var entityContext = serviceProvider.GetRequiredService();
+await entityContext.Database.EnsureCreatedAsync();
+entityContext.ApplyMigrations();
+
+var viewModelContext = serviceProvider.GetRequiredService();
+await viewModelContext.Database.EnsureCreatedAsync();
+viewModelContext.ApplyMigrations();
+
+// Use the aggregate
+var aggregateFactory = serviceProvider.GetRequiredService();
+var accountAggregate = await aggregateFactory.Create();
+
+accountAggregate.CreateAccount(999, "John Doe", 1000m);
+accountAggregate.Deposit(999, 500m);
+accountAggregate.Withdraw(999, 200m);
+
+// Give async processing time to complete
+await Task.Delay(500);
+
+// Query the read model
+var viewModelStore = serviceProvider.GetRequiredService();
+var accountView = await viewModelStore.Find(999);
+
+Console.WriteLine($"Account: {accountView.AccountName}");
+Console.WriteLine($"Balance: {accountView.CurrentBalance:C}");
+Console.WriteLine($"Transactions: {accountView.TransactionCount}");
+```
+
+For complete examples including ASP.NET Core, PostgreSQL, and production configurations, see [EntityFramework Usage Examples](#entityframework-usage-examples).
+
+---
+
+## Advanced Features
+
+### Event Replay
+
+SourceFlow.Net provides built-in command replay functionality for debugging and state reconstruction:
+
+```csharp
+var accountAggregate = serviceProvider.GetRequiredService();
+
+// Replay all commands for an aggregate
+await accountAggregate.ReplayHistory(accountId);
+
+// The framework automatically handles:
+// 1. Loading commands from store
+// 2. Marking commands as replay
+// 3. Re-executing command handlers
+// 4. Updating projections
+```
+
+### Metadata and Auditing
+
+Every command and event includes rich metadata to add producer and consumer centric custom properties.
+
+```csharp
+public interface IMetadata
+{
+ Guid EventId { get; set; }
+ bool IsReplay { get; set; }
+ DateTime OccurredOn { get; set; }
+ int SequenceNo { get; set; }
+ IDictionary Properties { get; set; }
+}
+```
+
+### Store Adapters
+
+SourceFlow provides high-level adapters for common operations:
+
+```csharp
+// Entity Store Adapter
+public interface IEntityStoreAdapter
+{
+ Task Persist(TEntity entity) where TEntity : class, IEntity;
+ Task Get(int id) where TEntity : class, IEntity;
+}
+
+// ViewModel Store Adapter
+public interface IViewModelStoreAdapter
+{
+ Task Push(TViewModel model) where TViewModel : class, IViewModel;
+ Task Find(int id) where TViewModel : class, IViewModel;
+}
+
+// Command Store Adapter
+public interface ICommandStoreAdapter
+{
+ Task Commit(ICommand command);
+ Task> Retrieve(int entityId);
+}
+```
+
+---
+
+## Performance and Observability
+
+SourceFlow.Net includes comprehensive production-ready features for monitoring, fault tolerance, and high-performance scenarios.
+
+### OpenTelemetry Integration
+
+Built-in support for distributed tracing and metrics collection at scale.
+
+#### Features
+
+- **Distributed Tracing**: Automatically track command execution, event dispatching, and store operations
+- **Metrics Collection**: Monitor command rates, saga executions, entity creations, and operation durations
+- **Multiple Exporters**: Support for Console, OTLP (Jaeger, Zipkin), and custom exporters
+- **Minimal Overhead**: <1ms latency impact, <2% CPU overhead
+
+#### Quick Setup
+
+**Development (Console Exporter):**
+```csharp
+using SourceFlow.Observability;
+using OpenTelemetry;
+
+var services = new ServiceCollection();
+
+// Enable observability with console output
+services.AddSourceFlowTelemetry(
+ serviceName: "MyEventSourcedApp",
+ serviceVersion: "1.0.0");
+
+services.AddOpenTelemetry()
+ .AddSourceFlowConsoleExporter();
+
+services.UseSourceFlow();
+```
+
+**Production (OTLP Exporter):**
+```csharp
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "ProductionApp";
+ options.ServiceVersion = "1.0.0";
+});
+
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter("http://localhost:4317")
+ .AddSourceFlowResourceAttributes(
+ ("environment", "production"),
+ ("region", "us-east-1")
+ );
+```
+
+#### Instrumented Operations
+
+All core operations are automatically traced:
+
+**Command Operations:**
+- `sourceflow.commandbus.dispatch` - Command dispatch and persistence
+- `sourceflow.commanddispatcher.send` - Command distribution to sagas
+- `sourceflow.domain.command.append` - Command persistence
+- `sourceflow.domain.command.load` - Command loading
+
+**Event Operations:**
+- `sourceflow.eventqueue.enqueue` - Event queuing
+- `sourceflow.eventdispatcher.dispatch` - Event distribution
+
+**Store Operations:**
+- `sourceflow.entitystore.persist` / `get` / `delete` - Entity operations
+- `sourceflow.viewmodelstore.persist` / `find` / `delete` - ViewModel operations
+
+#### Metrics Collected
+
+- `sourceflow.domain.commands.executed` - Counter of executed commands
+- `sourceflow.domain.sagas.executed` - Counter of saga executions
+- `sourceflow.domain.entities.created` - Counter of entity creations
+- `sourceflow.domain.operation.duration` - Histogram of operation durations (ms)
+- `sourceflow.domain.serialization.duration` - Histogram of serialization performance
+
+#### Integration with Existing Telemetry
+
+```csharp
+services.AddOpenTelemetry()
+ .WithTracing(builder => builder
+ .AddSource("SourceFlow.Domain") // Add SourceFlow
+ .AddAspNetCoreInstrumentation()
+ .AddHttpClientInstrumentation()
+ .AddOtlpExporter())
+ .WithMetrics(builder => builder
+ .AddMeter("SourceFlow.Domain") // Add SourceFlow
+ .AddAspNetCoreInstrumentation()
+ .AddPrometheusExporter());
+```
+
+### ArrayPool Memory Optimization
+
+Dramatically reduce memory allocations in high-throughput scenarios using `ArrayPool`.
+
+#### Performance Benefits
+
+**Memory Allocation Reduction:**
+- Before: ~40MB allocations for 10,000 commands
+- After: <1MB allocations for 10,000 commands
+- **Result: ~40x reduction in allocations**
+
+**GC Pressure Reduction:**
+- Gen 0 Collections: ↓70%
+- Gen 1 Collections: ↓50%
+- Gen 2 Collections: ↓30%
+
+**Throughput Improvements:**
+- Command Throughput: +25-40%
+- Event Dispatching: +30-50%
+- Serialization: +20-35%
+
+#### Features
+
+- **Task Buffer Pooling**: Reduces allocations in parallel task execution
+- **JSON Serialization Pooling**: Reuses byte buffers for JSON operations
+- **Zero Configuration**: Works automatically, no code changes required
+- **Production Tested**: Optimized for extreme throughput scenarios
+
+#### Optimized Components
+
+**TaskBufferPool:**
+- Pools task arrays for parallel execution
+- Used in `CommandDispatcher` and `EventDispatcher`
+- Automatic buffer rental and return
+
+**ByteArrayPool:**
+- Pools byte arrays for JSON serialization
+- Used in `CommandStoreAdapter`
+- Custom `IBufferWriter` implementation
+
+ArrayPool optimizations are automatically applied to:
+- Command serialization/deserialization
+- Event dispatching (parallel task execution)
+- Store adapter operations
+
+### Resilience with Polly (Entity Framework)
+
+The Entity Framework integration includes Polly-based resilience patterns for fault tolerance.
+
+#### Features
+
+- **Retry Policy**: Automatic retry with exponential backoff and jitter
+- **Circuit Breaker**: Prevents cascading failures
+- **Timeout Policy**: Enforces maximum execution time
+
+#### Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Enable resilience
+ options.Resilience.Enabled = true;
+
+ // Retry configuration
+ options.Resilience.Retry.MaxRetryAttempts = 3;
+ options.Resilience.Retry.BaseDelayMs = 1000;
+ options.Resilience.Retry.UseExponentialBackoff = true;
+ options.Resilience.Retry.UseJitter = true;
+
+ // Circuit breaker configuration
+ options.Resilience.CircuitBreaker.Enabled = true;
+ options.Resilience.CircuitBreaker.FailureThreshold = 5;
+ options.Resilience.CircuitBreaker.BreakDurationMs = 30000;
+
+ // Timeout configuration
+ options.Resilience.Timeout.Enabled = true;
+ options.Resilience.Timeout.TimeoutMs = 30000;
+});
+```
+
+#### Benefits
+
+- **Transient Failure Handling**: Automatically recovers from temporary issues
+- **Cascading Failure Prevention**: Circuit breaker stops calling failing services
+- **Resource Protection**: Timeouts prevent hanging operations
+- **Self-Healing**: System automatically recovers when service becomes available
+
+### Entity Framework Observability
+
+Additional observability features specific to Entity Framework stores.
+
+#### Configuration
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Configure observability
+ options.Observability.Enabled = true;
+ options.Observability.ServiceName = "MyApplication";
+ options.Observability.ServiceVersion = "1.0.0";
+
+ // Tracing configuration
+ options.Observability.Tracing.Enabled = true;
+ options.Observability.Tracing.TraceDatabaseOperations = true;
+ options.Observability.Tracing.IncludeSqlInTraces = false; // Enable for debugging
+ options.Observability.Tracing.SamplingRatio = 0.1; // Sample 10% in production
+
+ // Metrics configuration
+ options.Observability.Metrics.Enabled = true;
+ options.Observability.Metrics.CollectDatabaseMetrics = true;
+});
+
+// Configure exporters
+services.AddOpenTelemetry()
+ .WithTracing(tracing => tracing
+ .AddSource("SourceFlow.EntityFramework")
+ .AddEntityFrameworkCoreInstrumentation()
+ .AddJaegerExporter())
+ .WithMetrics(metrics => metrics
+ .AddMeter("SourceFlow.EntityFramework")
+ .AddPrometheusExporter());
+```
+
+#### Additional Traces
+
+- `sourceflow.ef.command.append` - EF command storage
+- `sourceflow.ef.command.load` - EF command loading
+- `sourceflow.ef.entity.persist` - EF entity persistence
+- `sourceflow.ef.viewmodel.persist` - EF view model persistence
+
+#### Additional Metrics
+
+- `sourceflow.commands.appended` - EF command append counter
+- `sourceflow.commands.loaded` - EF command load counter
+- `sourceflow.entities.persisted` - EF entity persistence counter
+- `sourceflow.viewmodels.persisted` - EF view model persistence counter
+- `sourceflow.database.connections` - Active connection gauge
+
+### Production Configuration Examples
+
+**Development:**
+```csharp
+services.AddSourceFlowTelemetry("DevApp", "1.0.0");
+services.AddOpenTelemetry()
+ .AddSourceFlowConsoleExporter();
+
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = "Data Source=dev.db";
+ options.Resilience.Enabled = false; // Easier debugging
+ options.Observability.Enabled = true;
+ options.Observability.Tracing.IncludeSqlInTraces = true;
+ options.Observability.Tracing.SamplingRatio = 1.0; // Trace everything
+});
+```
+
+**Production:**
+```csharp
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "ProductionApp";
+ options.ServiceVersion = "1.0.0";
+});
+
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter(otlpEndpoint)
+ .AddSourceFlowResourceAttributes(
+ ("environment", "production"),
+ ("deployment.region", region)
+ );
+
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Production resilience settings
+ options.Resilience.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 3;
+ options.Resilience.CircuitBreaker.Enabled = true;
+ options.Resilience.CircuitBreaker.FailureThreshold = 10;
+
+ // Production observability settings
+ options.Observability.Enabled = true;
+ options.Observability.Tracing.IncludeSqlInTraces = false;
+ options.Observability.Tracing.SamplingRatio = 0.1; // Sample 10%
+});
+```
+
+**High-Throughput:**
+```csharp
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "HighThroughputApp";
+});
+
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter(otlpEndpoint)
+ .ConfigureSourceFlowBatchProcessing(
+ maxQueueSize: 2048,
+ maxExportBatchSize: 512,
+ scheduledDelayMilliseconds: 5000
+ );
+
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+
+ // Optimized for throughput
+ options.Resilience.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 2;
+ options.Resilience.Retry.BaseDelayMs = 500;
+
+ // Reduced overhead
+ options.Observability.Enabled = true;
+ options.Observability.Tracing.SamplingRatio = 0.01; // Sample 1%
+});
+
+// ArrayPool optimizations are automatically applied
+```
+
+### Monitoring Dashboard Queries
+
+Use these queries in Grafana/Prometheus:
+
+**Average Command Processing Time:**
+```promql
+rate(sourceflow_domain_operation_duration_sum{operation="sourceflow.commandbus.dispatch"}[5m])
+/ rate(sourceflow_domain_operation_duration_count{operation="sourceflow.commandbus.dispatch"}[5m])
+```
+
+**Command Throughput:**
+```promql
+rate(sourceflow_domain_commands_executed[5m])
+```
+
+**Serialization Performance (P95):**
+```promql
+histogram_quantile(0.95,
+ rate(sourceflow_domain_serialization_duration_bucket[5m])
+)
+```
+
+### Package Dependencies
+
+**Core SourceFlow:**
+- `OpenTelemetry` (1.14.0)
+- `OpenTelemetry.Api` (1.14.0)
+- `OpenTelemetry.Exporter.Console` (1.14.0)
+- `OpenTelemetry.Exporter.OpenTelemetryProtocol` (1.14.0)
+- `OpenTelemetry.Extensions.Hosting` (1.14.0)
+- `Microsoft.Extensions.DependencyInjection.Abstractions` (10.0.0)
+- `Microsoft.Extensions.Logging.Abstractions` (10.0.0)
+
+**Entity Framework Stores:**
+- `Microsoft.EntityFrameworkCore` (9.0.0)
+- `Polly` (8.4.2) - For resilience patterns
+- `OpenTelemetry.Instrumentation.EntityFrameworkCore` (1.0.0-beta.12)
+
+All packages are free from known vulnerabilities (as of November 2025).
+
+### Additional Resources
+
+- **OpenTelemetry Documentation**: [https://opentelemetry.io/docs/](https://opentelemetry.io/docs/)
+- **ArrayPool Documentation**: [https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1](https://docs.microsoft.com/en-us/dotnet/api/system.buffers.arraypool-1)
+- **Polly Documentation**: [https://github.com/App-vNext/Polly](https://github.com/App-vNext/Polly)
+- **SourceFlow.Net OBSERVABILITY_AND_PERFORMANCE.md**: Detailed performance documentation
+- **SourceFlow.Stores.EntityFramework ENHANCEMENTS.md**: EF-specific enhancements guide
+
+---
+
+## Best Practices
+
+### 1. Command Design
+
+**Always include a parameterless constructor** for serialization support:
+
+```csharp
+// ✅ Good: Specific, intention-revealing commands with proper constructors
+public class WithdrawMoney : Command
+{
+ // Required for deserialization from command store
+ public WithdrawMoney() : base() { }
+
+ public WithdrawMoney(WithdrawPayload payload) : base(payload) { }
+}
+
+public class DepositMoney : Command
+{
+ // Required for deserialization from command store
+ public DepositMoney() : base() { }
+
+ public DepositMoney(DepositPayload payload) : base(payload) { }
+}
+
+// ❌ Bad: Generic, unclear commands
+public class UpdateAccount : Command { }
+
+// ❌ Bad: Missing parameterless constructor
+public class TransferMoney : Command
+{
+ public TransferMoney(TransferPayload payload) : base(payload) { }
+ // This command cannot be deserialized from the command store!
+}
+```
+
+**Key Requirements:**
+- Use specific, intention-revealing names
+- Always include a public parameterless constructor
+- Include a constructor that accepts the payload
+- Keep commands immutable after creation
+
+### 2. Event Granularity
+
+```csharp
+// ✅ Good: Fine-grained, specific events
+public class AccountCreated : Event { }
+public class AccountCredited : Event { }
+public class AccountDebited : Event { }
+
+// ❌ Bad: Coarse-grained, generic events
+public class AccountChanged : Event { }
+```
+
+### 3. Saga Responsibility
+
+```csharp
+// ✅ Good: Single responsibility
+public class AccountSaga : Saga,
+ IHandles,
+ IHandles
+{
+ // Handles account lifecycle only
+}
+
+// ❌ Bad: Multiple responsibilities
+public class MegaSaga : Saga,
+ IHandles,
+ IHandles,
+ IHandles
+{
+ // Too many responsibilities
+}
+```
+
+### 4. Type Registration
+
+```csharp
+// ✅ Good: Register types early, before building service provider
+EntityDbContext.RegisterAssembly(typeof(BankAccount).Assembly);
+ViewModelDbContext.RegisterAssembly(typeof(AccountViewModel).Assembly);
+
+var serviceProvider = services.BuildServiceProvider();
+
+// Apply migrations after database creation
+entityContext.Database.EnsureCreated();
+entityContext.ApplyMigrations();
+
+// ❌ Bad: Relying solely on auto-discovery
+var serviceProvider = services.BuildServiceProvider();
+// Types may not be discovered reliably
+```
+
+### 5. Error Handling
+
+```csharp
+public class AccountSaga : Saga
+{
+ public async Task Handle(WithdrawMoney command)
+ {
+ try
+ {
+ var account = await repository.Get(command.Payload.Id);
+
+ // Validate business rules
+ if (account.IsClosed)
+ throw new AccountClosedException($"Account {account.Id} is closed");
+
+ if (account.Balance < command.Payload.Amount)
+ throw new InsufficientFundsException($"Insufficient funds in account {account.Id}");
+
+ // Process transaction
+ account.Balance -= command.Payload.Amount;
+ await repository.Persist(account);
+ await Raise(new MoneyWithdrawn(account));
+ }
+ catch (Exception ex)
+ {
+ logger.LogError(ex, "Failed to process withdrawal for account {AccountId}",
+ command.Payload.Id);
+
+ // Publish failure event if needed
+ await Raise(new WithdrawalFailed(new WithdrawalFailureDetails
+ {
+ AccountId = command.Payload.Id,
+ Amount = command.Payload.Amount,
+ Reason = ex.Message
+ }));
+
+ throw;
+ }
+ }
+}
+```
+
+### 6. Database Migrations
+
+```csharp
+// ✅ Good: Use ApplyMigrations for dynamic types
+entityContext.Database.EnsureCreated();
+entityContext.ApplyMigrations(); // Creates tables for registered types
+
+viewModelContext.Database.EnsureCreated();
+viewModelContext.ApplyMigrations(); // Creates tables for view models
+
+// For production: Use EF Core migrations for static schema
+// dotnet ef migrations add InitialCreate
+// dotnet ef database update
+```
+
+### 7. Production Monitoring
+
+```csharp
+// ✅ Good: Enable observability in production
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "ProductionApp";
+ options.ServiceVersion = Assembly.GetExecutingAssembly()
+ .GetName().Version.ToString();
+});
+
+// Configure appropriate sampling rate
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter(otlpEndpoint)
+ .AddSourceFlowResourceAttributes(
+ ("environment", Environment.GetEnvironmentVariable("ENVIRONMENT"))
+ );
+
+// ❌ Bad: No observability in production
+services.UseSourceFlow();
+// Can't diagnose performance issues or failures
+```
+
+### 8. Resilience Configuration
+
+```csharp
+// ✅ Good: Enable resilience for production databases
+services.AddSourceFlowEfStores(options =>
+{
+ options.DefaultConnectionString = connectionString;
+ options.Resilience.Enabled = true;
+ options.Resilience.Retry.MaxRetryAttempts = 3;
+ options.Resilience.CircuitBreaker.Enabled = true;
+});
+
+// ❌ Bad: No resilience (will fail on transient errors)
+services.AddSourceFlowEfStores(connectionString);
+```
+
+### 9. Command Serialization Requirements
+
+```csharp
+// ✅ Good: Command with parameterless constructor
+public class ProcessPayment : Command
+{
+ // REQUIRED: Parameterless constructor for deserialization
+ public ProcessPayment() : base() { }
+
+ public ProcessPayment(PaymentPayload payload) : base(payload) { }
+}
+
+// ❌ Bad: Missing parameterless constructor
+public class ProcessPayment : Command
+{
+ public ProcessPayment(PaymentPayload payload) : base(payload) { }
+ // Will throw MissingMethodException during command replay!
+}
+
+// ✅ Good: Payload classes don't need parameterless constructors
+public class PaymentPayload : IPayload
+{
+ public int Id { get; set; }
+ public decimal Amount { get; set; }
+ public string Currency { get; set; }
+}
+```
+
+**Important Notes:**
+- **Commands MUST have a public parameterless constructor** for deserialization from the command store
+- Payload classes use property setters for deserialization (no parameterless constructor required)
+- Without a parameterless constructor, command replay and aggregate reconstruction will fail
+- The Entity Framework CommandStoreAdapter uses reflection to recreate command instances
+
+---
+
+## FAQ
+
+### Q: How does SourceFlow.Net handle persistence?
+
+**A:** SourceFlow.Net uses a store abstraction pattern with multiple implementation options:
+
+- **In-Memory Stores**: Built-in for testing and prototyping
+- **Entity Framework Stores**: Production-ready with support for SQL Server, PostgreSQL, SQLite, etc.
+- **Custom Stores**: Implement `ICommandStore`, `IEntityStore`, and `IViewModelStore` for your own persistence
+
+### Q: Can I use different databases for commands, entities, and view models?
+
+**A:** Yes! The Entity Framework integration supports separate databases:
+
+```csharp
+services.AddSourceFlowEfStoresWithCustomProviders(
+ commandContextConfig: options => options.UseSqlServer("..."),
+ entityContextConfig: options => options.UsePostgreSql("..."),
+ viewModelContextConfig: options => options.UseSqlite("...")
+);
+```
+
+### Q: How do I handle dynamic entity and view model types?
+
+**A:** Use the type registration and migration system:
+
+1. Register types before building the service provider
+2. Call `EnsureCreated()` to create the base schema
+3. Call `ApplyMigrations()` to create tables for registered types
+
+```csharp
+EntityDbContext.RegisterEntityType();
+ViewModelDbContext.RegisterViewModelType();
+
+// Build service provider...
+
+entityContext.Database.EnsureCreated();
+entityContext.ApplyMigrations();
+```
+
+### Q: Why do my commands need a parameterless constructor?
+
+**A:** The CommandStoreAdapter uses reflection to deserialize commands from the database. When replaying commands, it needs to create instances without knowing the payload in advance.
+
+**Required pattern:**
+
+```csharp
+public class CreateAccount : Command
+{
+ // Required for deserialization
+ public CreateAccount() : base() { }
+
+ // Used for creating new commands
+ public CreateAccount(CreateAccountPayload payload) : base(payload) { }
+}
+```
+
+Without the parameterless constructor, command replay will fail with a `MissingMethodException`.
+
+### Q: What database providers are supported?
+
+**A:** SourceFlow.Stores.EntityFramework supports any EF Core provider:
+
+- SQL Server (default)
+- PostgreSQL (via Npgsql.EntityFrameworkCore.PostgreSQL)
+- SQLite (via Microsoft.EntityFrameworkCore.Sqlite)
+- MySQL (via Pomelo.EntityFrameworkCore.MySql)
+- In-Memory (via Microsoft.EntityFrameworkCore.InMemoryDatabase)
+- And more...
+
+### Q: How do I test with SourceFlow.Net?
+
+**A:** Use in-memory databases for fast, isolated tests:
+
+```csharp
+[SetUp]
+public void Setup()
+{
+ EntityDbContext.RegisterEntityType();
+ ViewModelDbContext.RegisterViewModelType();
+
+ var connection = new SqliteConnection("DataSource=:memory:");
+ connection.Open();
+
+ services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseSqlite(connection));
+
+ // Build and setup...
+}
+```
+
+### Q: Why use the "T" prefix for table names?
+
+**A:** The "T" prefix distinguishes dynamically created tables from EF Core's built-in tables, making it clear which tables are part of your domain model versus infrastructure.
+
+### Q: Should I enable observability in production?
+
+**A:** Yes! Observability has minimal overhead (<1ms latency, <2% CPU) and provides invaluable insights:
+- Distributed tracing helps debug issues across services
+- Metrics help identify performance bottlenecks
+- Sampling (10%) provides good coverage with minimal cost
+
+```csharp
+services.AddSourceFlowTelemetry(options =>
+{
+ options.Enabled = true;
+ options.ServiceName = "ProductionApp";
+});
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter(otlpEndpoint);
+```
+
+### Q: When should I use resilience patterns?
+
+**A:** Always enable resilience in production for database operations:
+- Retry policies handle transient network failures
+- Circuit breakers prevent cascading failures
+- Timeouts prevent hanging operations
+
+```csharp
+services.AddSourceFlowEfStores(options =>
+{
+ options.Resilience.Enabled = true;
+});
+```
+
+### Q: How much does ArrayPool improve performance?
+
+**A:** In high-throughput scenarios (>1000 commands/second):
+- **Memory**: 40x reduction in allocations (40MB → <1MB for 10K commands)
+- **GC**: 70% reduction in Gen0 collections
+- **Throughput**: 25-40% improvement
+- **Zero configuration**: Works automatically once enabled
+
+ArrayPool optimizations are built-in and automatically applied to command serialization and event dispatching.
+
+### Q: How do I handle schema changes?
+
+**A:** For production applications:
+
+1. Use EF Core migrations for base schema
+2. Use `ApplyMigrations()` for dynamic types
+3. Version your entities and view models
+4. Implement upcasting for old events
+
+For development/testing:
+
+1. Use `Database.EnsureDeleted()` and `EnsureCreated()`
+2. Use in-memory databases that reset on each test
+
+### Q: Can I use SourceFlow.EntityFramework with MySQL or other databases?
+
+**A:** Yes! Use `AddSourceFlowEfStoresWithCustomProvider` for any EF Core supported database:
+
+```csharp
+// MySQL
+var serverVersion = new MySqlServerVersion(new Version(8, 0, 21));
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseMySql(connectionString, serverVersion));
+
+// SQLite
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseSqlite("Data Source=sourceflow.db"));
+
+// PostgreSQL
+services.AddSourceFlowEfStoresWithCustomProvider(options =>
+ options.UseNpgsql(connectionString));
+```
+
+The `AddSourceFlowEfStores` methods without "CustomProvider" use SQL Server by default.
+
+### Q: What's the difference between EnsureCreated() and ApplyMigrations()?
+
+**A:**
+- `EnsureCreated()`: Creates the database and base schema (Commands, fixed tables)
+- `ApplyMigrations()`: Creates tables for dynamically registered entities and view models
+
+Always call both in the correct order:
+
+```csharp
+await entityContext.Database.EnsureCreatedAsync(); // Create database
+entityContext.ApplyMigrations(); // Create dynamic tables
+```
+
+### Q: How do I configure EF Core 9.0 for testing to avoid provider conflicts?
+
+**A:** When testing with multiple DbContext providers (e.g., SQLite for tests, SQL Server for production), use `EnableServiceProviderCaching(false)`:
+
+```csharp
+services.AddDbContext(options =>
+ options.UseSqlite(connection)
+ .EnableServiceProviderCaching(false)); // Required for EF Core 9.0
+```
+
+This prevents the "multiple provider" error when using different providers in the same service collection.
+
+### Q: Should I register stores manually or use AddSourceFlowEfStores?
+
+**A:** Use `AddSourceFlowEfStores` for production. Only register manually for special cases like:
+
+- Testing scenarios requiring specific service configuration
+- Custom implementations of resilience or telemetry services
+- Avoiding provider conflicts in test setups
+
+Example manual registration:
+
+```csharp
+var efOptions = new SourceFlowEfOptions();
+services.AddSingleton(efOptions);
+services.AddScoped();
+services.AddScoped();
+services.AddScoped();
+services.AddScoped();
+services.AddScoped();
+```
+
+### Q: How do table naming conventions affect my database schema?
+
+**A:** Table naming conventions transform entity type names into table names:
+
+```csharp
+// Default (PascalCase, no prefix/suffix)
+BankAccount → BankAccount
+
+// Snake case with pluralization
+options.EntityTableNaming.Casing = TableNameCasing.SnakeCase;
+options.EntityTableNaming.Pluralize = true;
+BankAccount → bank_accounts
+
+// With schema
+options.EntityTableNaming.UseSchema = true;
+options.EntityTableNaming.SchemaName = "domain";
+BankAccount → domain.BankAccount
+
+// Combined
+BankAccount → domain.bank_accounts
+```
+
+Set naming conventions BEFORE calling `ApplyMigrations()` to ensure tables are created with the correct names.
+
+---
+
+## Production Considerations
+
+### Performance Optimization
+
+1. **Use Separate Databases**: Split command, entity, and view model stores across different databases
+2. **Enable Connection Pooling**: Configure appropriate connection pool sizes
+3. **Optimize Queries**: Use AsNoTracking() for read-only queries
+4. **Batch Operations**: Use bulk insert/update operations where applicable
+5. **Enable ArrayPool**: Automatically enabled for high-throughput scenarios (40x reduction in allocations)
+6. **Configure Observability**: Use appropriate sampling rates for production (1-10%)
+7. **Enable Resilience**: Use Polly policies for fault tolerance in production
+
+### Monitoring
+
+```csharp
+// Health checks
+services.AddHealthChecks()
+ .AddDbContextCheck("commandstore")
+ .AddDbContextCheck("entitystore")
+ .AddDbContextCheck("viewmodelstore");
+
+// OpenTelemetry metrics and tracing
+services.AddSourceFlowTelemetry("ProductionApp", "1.0.0");
+services.AddOpenTelemetry()
+ .AddSourceFlowOtlpExporter("http://localhost:4317");
+
+// Monitor key metrics:
+// - Command throughput: sourceflow.domain.commands.executed
+// - Operation latency: sourceflow.domain.operation.duration (P50/P95/P99)
+// - Circuit breaker state: polly.circuit_breaker.state
+// - GC pressure: dotnet.gc.collections (reduced with ArrayPool)
+```
+
+### Deployment
+
+1. **Migrations**: Apply EF Core migrations during deployment
+2. **Connection Strings**: Use environment-specific configuration
+3. **Logging**: Configure appropriate logging levels
+4. **Error Handling**: Implement global exception handling
+
+### Common Issues and Solutions
+
+**Command Deserialization Failures:**
+- **Symptom**: `MissingMethodException` or `InvalidOperationException` during command replay
+- **Cause**: Command class missing parameterless constructor
+- **Solution**: Add public parameterless constructor to all command classes
+
+```csharp
+// Fix: Add parameterless constructor
+public class MyCommand : Command
+{
+ public MyCommand() : base() { } // Required!
+ public MyCommand(MyPayload payload) : base(payload) { }
+}
+```
+
+**Entity Tracking Conflicts:**
+- **Symptom**: "Instance already being tracked" errors
+- **Cause**: Multiple entity instances with same ID in EF change tracker
+- **Solution**: Use `AsNoTracking()` for read operations or detach entities after save
+
+**EF Core 9.0 Provider Conflicts (Testing):**
+- **Symptom**: "An error occurred accessing the database provider factory"
+- **Cause**: Multiple providers registered in same service collection
+- **Solution**: Use `EnableServiceProviderCaching(false)` in test configurations
+
+**Migration Failures:**
+- **Symptom**: Tables not created for entities or view models
+- **Cause**: Types not registered before calling `ApplyMigrations()`
+- **Solution**: Register types using `EntityDbContext.RegisterAssembly()` before building service provider
+
+---
+
+## Community and Support
+
+### Resources
+
+- **GitHub Repository**: [https://github.com/CodeShayk/SourceFlow.Net](https://github.com/CodeShayk/SourceFlow.Net)
+- **Documentation**: [https://github.com/CodeShayk/SourceFlow.Net/wiki](https://github.com/CodeShayk/SourceFlow.Net/wiki)
+- **Issues**: [https://github.com/CodeShayk/SourceFlow.Net/issues](https://github.com/CodeShayk/SourceFlow.Net/issues)
+- **Discussions**: [https://github.com/CodeShayk/SourceFlow.Net/discussions](https://github.com/CodeShayk/SourceFlow.Net/discussions)
+
+### License
+
+SourceFlow.Net is released under the MIT License, making it free for both commercial and open-source use.
+
+---
+
+## Conclusion
+
+SourceFlow.Net provides a robust, scalable foundation for building event-sourced applications with .NET. By combining Event Sourcing, Domain-Driven Design, and CQRS patterns with flexible Entity Framework persistence, it enables developers to create maintainable, auditable, and performant systems.
+
+**Start your journey with SourceFlow.Net today and build better software with events as your foundation!**
diff --git a/src/SourceFlow.ConsoleApp/Aggregates/AccountAggregate.cs b/src/SourceFlow.ConsoleApp/Aggregates/AccountAggregate.cs
deleted file mode 100644
index 9706ec7..0000000
--- a/src/SourceFlow.ConsoleApp/Aggregates/AccountAggregate.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-using SourceFlow.Aggregate;
-using SourceFlow.ConsoleApp.Commands;
-using SourceFlow.ConsoleApp.Events;
-
-namespace SourceFlow.ConsoleApp.Aggregates
-{
- public class AccountAggregate : Aggregate,
- ISubscribes
-
- {
- public void CreateAccount(int accountId, string holder, decimal amount)
- {
- Send(new CreateAccount(new Payload
- {
- Id = accountId,
- AccountName = holder,
- InitialAmount = amount
- }));
- }
-
- public void Deposit(int accountId, decimal amount)
- {
- Send(new DepositMoney(new TransactPayload
- {
- Id = accountId,
- Amount = amount,
- Type = TransactionType.Deposit
- }));
- }
-
- public void Withdraw(int accountId, decimal amount)
- {
- Send(new WithdrawMoney(new TransactPayload
- {
- Id = accountId,
- Amount = amount,
- Type = TransactionType.Withdrawal
- }));
- }
-
- public void Close(int accountId, string reason)
- {
- Send(new CloseAccount(new ClosurePayload
- {
- Id = accountId,
- ClosureReason = reason
- }));
- }
-
- public Task Handle(AccountCreated @event)
- {
- return Send(new ActivateAccount(new ActivationPayload
- {
- Id = @event.Payload.Id,
- ActiveOn = DateTime.UtcNow,
- }));
- }
- }
-}
\ No newline at end of file
diff --git a/src/SourceFlow.ConsoleApp/Commands/ActivateAccount.cs b/src/SourceFlow.ConsoleApp/Commands/ActivateAccount.cs
deleted file mode 100644
index fd78929..0000000
--- a/src/SourceFlow.ConsoleApp/Commands/ActivateAccount.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using SourceFlow.Messaging;
-
-namespace SourceFlow.ConsoleApp.Commands
-{
- public class ActivateAccount : Command
- {
- public ActivateAccount(ActivationPayload payload) : base(payload)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/SourceFlow.ConsoleApp/Commands/CloseAccount.cs b/src/SourceFlow.ConsoleApp/Commands/CloseAccount.cs
deleted file mode 100644
index b8ed5f9..0000000
--- a/src/SourceFlow.ConsoleApp/Commands/CloseAccount.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using SourceFlow.Messaging;
-
-namespace SourceFlow.ConsoleApp.Commands
-{
- public class CloseAccount : Command
- {
- public CloseAccount(ClosurePayload payload) : base(payload)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/SourceFlow.ConsoleApp/Commands/CreateAccount.cs b/src/SourceFlow.ConsoleApp/Commands/CreateAccount.cs
deleted file mode 100644
index 8251038..0000000
--- a/src/SourceFlow.ConsoleApp/Commands/CreateAccount.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using SourceFlow.Messaging;
-
-namespace SourceFlow.ConsoleApp.Commands
-{
- public class CreateAccount : Command
- {
- public CreateAccount(Payload payload) : base(payload)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/SourceFlow.ConsoleApp/Commands/DepositMoney.cs b/src/SourceFlow.ConsoleApp/Commands/DepositMoney.cs
deleted file mode 100644
index 49153a0..0000000
--- a/src/SourceFlow.ConsoleApp/Commands/DepositMoney.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using SourceFlow.Messaging;
-
-namespace SourceFlow.ConsoleApp.Commands
-{
- public class DepositMoney : Command
- {
- public DepositMoney(TransactPayload payload) : base(payload)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/src/SourceFlow.ConsoleApp/Commands/WithdrawMoney.cs b/src/SourceFlow.ConsoleApp/Commands/WithdrawMoney.cs
deleted file mode 100644
index a546eeb..0000000
--- a/src/SourceFlow.ConsoleApp/Commands/WithdrawMoney.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using SourceFlow.Messaging;
-
-namespace SourceFlow.ConsoleApp.Commands
-{
- public class WithdrawMoney : Command