diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc9d82b..34ec761 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,16 @@ All notable changes to this project 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).
+## [3.1.0] - 2025-04-23
+### Added
+- Added new extension methods for dependency injection:
+ - `AddWorkflows()` - Registers all workflow components (validations, activities, and workflows)
+ - `AddWorkflowValidations()` - Registers workflow validations only
+ - `AddWorkflowActivities()` - Registers workflow activities only
+
+- Added support for automatic assembly scanning to discover workflow components
+- Added the ability to specify service lifetime for workflow registrations
+
## [3.0.0] - 2025-05-01
### Added
@@ -34,7 +44,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Compatibility
-- All existing code using the deprecated methods will continue to work, but will show deprecation warnings
+- All existing code using the deprecated methods will continue to work but will show deprecation warnings
- To migrate, replace:
```csharp
diff --git a/Directory.Build.props b/Directory.Build.props
index a8250fe..30684bf 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -13,7 +13,7 @@
A .NET library for building robust, functional workflows and processing pipelines.
- 3.0.0
+ 3.1.0true
diff --git a/README.md b/README.md
index 9108603..4150d24 100644
--- a/README.md
+++ b/README.md
@@ -5,11 +5,14 @@
[](https://www.nuget.org/packages/Zooper.Bee/)
[](https://opensource.org/licenses/MIT)
-A flexible and powerful workflow library for .NET that allows you to define complex business processes with a fluent API.
+A flexible and powerful workflow library for .NET that allows you to define complex business processes with a fluent
+API.
## Overview
-Zooper.Bee lets you create workflows that process requests and produce either successful results or meaningful errors. The library uses a builder pattern to construct workflows with various execution patterns including sequential, conditional, parallel, and detached operations.
+Zooper.Bee lets you create workflows that process requests and produce either successful results or meaningful errors.
+The library uses a builder pattern to construct workflows with various execution patterns including sequential,
+conditional, parallel, and detached operations.
## Key Concepts
@@ -17,7 +20,7 @@ Zooper.Bee lets you create workflows that process requests and produce either su
- **Request**: The input data to the workflow
- **Payload**: Data that passes through and gets modified by workflow activities
- **Success**: The successful result of the workflow
-- **Error**: The error result if the workflow fails
+- **Error**: The errors result if the workflow fails
## Installation
@@ -90,7 +93,8 @@ Validates the incoming request before processing begins.
#### Activities
-Activities are the building blocks of a workflow. They process the payload and can produce either a success (with modified payload) or an error.
+Activities are the building blocks of a workflow. They process the payload and can produce either a success (with
+the modified payload) or an error.
```csharp
// Asynchronous activity
@@ -135,7 +139,8 @@ Activities that only execute if a condition is met.
#### Groups
-Organize related activities into logical groups. Groups can have conditions and always merge their results back to the main workflow.
+Organize related activities into logical groups. Groups can have conditions and always merge their results back to the
+main workflow.
```csharp
.Group(
@@ -149,7 +154,8 @@ Organize related activities into logical groups. Groups can have conditions and
#### Contexts with Local State
-Create a context with local state that is accessible to all activities within the context. This helps encapsulate related operations.
+Create a context with the local state that is accessible to all activities within the context. This helps encapsulate
+related operations.
```csharp
.WithContext(
@@ -188,7 +194,8 @@ Execute multiple groups of activities in parallel and merge the results.
#### Detached Execution
-Execute activities in the background without waiting for their completion. Results from detached activities are not merged back into the main workflow.
+Execute activities in the background without waiting for their completion. Results from detached activities are not
+merged back into the main workflow.
```csharp
.Detach(
@@ -269,6 +276,38 @@ Use conditions to determine which path to take in a workflow.
)
```
+## Dependency Injection Integration
+
+Zooper.Bee integrates seamlessly with .NET's dependency injection system. You can register all workflow components with
+a single extension method:
+
+```csharp
+// In Startup.cs or Program.cs
+services.AddWorkflows();
+```
+
+This will scan all assemblies and register:
+
+- All workflow validations
+- All workflow activities
+- All concrete workflow classes (classes ending with "Workflow")
+
+You can also register specific components:
+
+```csharp
+// Register only validations
+services.AddWorkflowValidations();
+
+// Register only activities
+services.AddWorkflowActivities();
+
+// Specify which assemblies to scan
+services.AddWorkflows(new[] { typeof(Program).Assembly });
+
+// Specify service lifetime (Singleton, Scoped, Transient)
+services.AddWorkflows(lifetime: ServiceLifetime.Singleton);
+```
+
## Performance Considerations
- Use `Parallel` for CPU-bound operations that can benefit from parallel execution
@@ -288,4 +327,4 @@ Use conditions to determine which path to take in a workflow.
## License
-MIT License (Copyright details here)
+MIT License (Copyright details here)
\ No newline at end of file
diff --git a/Zooper.Bee.Example/BranchingExample.cs b/Zooper.Bee.Example/BranchingExample.cs
index be76edc..321f7e7 100644
--- a/Zooper.Bee.Example/BranchingExample.cs
+++ b/Zooper.Bee.Example/BranchingExample.cs
@@ -92,7 +92,7 @@ private static Workflow new RegistrationSuccess(
payload.UserId,
payload.Email,
diff --git a/Zooper.Bee/Extensions/WorkflowActivitiesExtensions.cs b/Zooper.Bee/Extensions/WorkflowActivitiesExtensions.cs
new file mode 100644
index 0000000..e1cfa63
--- /dev/null
+++ b/Zooper.Bee/Extensions/WorkflowActivitiesExtensions.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Zooper.Bee.Interfaces;
+
+// ReSharper disable MemberCanBePrivate.Global
+
+namespace Zooper.Bee.Extensions;
+
+///
+/// Extension methods for registering workflow activities with dependency injection
+///
+public static class WorkflowActivitiesExtensions
+{
+ ///
+ /// Adds all workflow activities from the specified assemblies, or from all loaded assemblies if none specified
+ ///
+ /// The service collection
+ /// Optional list of assemblies to scan. If null or empty, scans all loaded assemblies
+ /// The service lifetime (defaults to Scoped)
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivities(
+ this IServiceCollection services,
+ IEnumerable? assemblies = null,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ // If no assemblies are specified, use all loaded assemblies
+ assemblies ??= AppDomain.CurrentDomain.GetAssemblies()
+ .Where(a => !a.IsDynamic && !a.FullName.StartsWith("System") && !a.FullName.StartsWith("Microsoft"));
+
+ // Register all IWorkflowActivity implementations
+ services.Scan(scan => scan
+ .FromAssemblies(assemblies)
+ .AddClasses(classes => classes.AssignableTo(typeof(IWorkflowActivity)))
+ .AsImplementedInterfaces()
+ .WithLifetime(lifetime)
+ );
+
+ // Register all IWorkflowActivities implementations
+ services.Scan(scan => scan
+ .FromAssemblies(assemblies)
+ .AddClasses(classes => classes.AssignableTo(typeof(IWorkflowActivities)))
+ .AsImplementedInterfaces()
+ .WithLifetime(lifetime)
+ );
+
+ return services;
+ }
+
+ ///
+ /// Adds all workflow activities from the assemblies containing the specified marker types
+ ///
+ /// The service collection
+ /// Types whose assemblies will be scanned
+ /// The service lifetime (defaults to Scoped)
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivitiesFromAssembliesContaining(
+ this IServiceCollection services,
+ IEnumerable markerTypes,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ if (markerTypes == null) throw new ArgumentNullException(nameof(markerTypes));
+
+ var assemblies = markerTypes.Select(t => t.Assembly).Distinct();
+ return services.AddWorkflowActivities(assemblies, lifetime);
+ }
+
+ ///
+ /// Adds all workflow activities from the assemblies containing the specified marker types
+ ///
+ /// The service collection
+ /// The service lifetime (defaults to Scoped)
+ /// Types whose assemblies will be scanned
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivitiesFromAssembliesContaining(
+ this IServiceCollection services,
+ ServiceLifetime lifetime,
+ params Type[] markerTypes)
+ {
+ return services.AddWorkflowActivitiesFromAssembliesContaining(markerTypes, lifetime);
+ }
+
+ ///
+ /// Adds all workflow activities from the assemblies containing the specified marker types
+ ///
+ /// The service collection
+ /// Types whose assemblies will be scanned
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivitiesFromAssembliesContaining(
+ this IServiceCollection services,
+ params Type[] markerTypes)
+ {
+ return services.AddWorkflowActivitiesFromAssembliesContaining(markerTypes, ServiceLifetime.Scoped);
+ }
+
+ ///
+ /// Adds all workflow activities from the assemblies containing the specified marker types
+ ///
+ /// First marker type whose assembly will be scanned
+ /// The service collection
+ /// The service lifetime (defaults to Scoped)
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivitiesFromAssembliesContaining(
+ this IServiceCollection services,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ return services.AddWorkflowActivitiesFromAssembliesContaining(
+ [
+ typeof(T1)
+ ],
+ lifetime
+ );
+ }
+
+ ///
+ /// Adds all workflow activities from the assemblies containing the specified marker types
+ ///
+ /// First marker type whose assembly will be scanned
+ /// Second marker type whose assembly will be scanned
+ /// The service collection
+ /// The service lifetime (defaults to Scoped)
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivitiesFromAssembliesContaining(
+ this IServiceCollection services,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ return services.AddWorkflowActivitiesFromAssembliesContaining(
+ [
+ typeof(T1), typeof(T2)
+ ],
+ lifetime
+ );
+ }
+
+ ///
+ /// Adds all workflow activities from the assemblies containing the specified marker types
+ ///
+ /// First marker type whose assembly will be scanned
+ /// Second marker type whose assembly will be scanned
+ /// Third marker type whose assembly will be scanned
+ /// The service collection
+ /// The service lifetime (defaults to Scoped)
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivitiesFromAssembliesContaining(
+ this IServiceCollection services,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ return services.AddWorkflowActivitiesFromAssembliesContaining(
+ [
+ typeof(T1), typeof(T2), typeof(T3)
+ ],
+ lifetime
+ );
+ }
+
+ ///
+ /// Adds all workflow activities of specific types from the specified assemblies, or from all loaded assemblies if none specified
+ ///
+ /// The type of payload the activities process
+ /// The type of error the activities might return
+ /// The service collection
+ /// Optional list of assemblies to scan. If null or empty, scans all loaded assemblies
+ /// The service lifetime (defaults to Scoped)
+ /// The service collection for chaining
+ public static IServiceCollection AddWorkflowActivities(
+ this IServiceCollection services,
+ IEnumerable? assemblies = null,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ // If no assemblies are specified, use all loaded assemblies
+ assemblies ??= AppDomain.CurrentDomain.GetAssemblies()
+ .Where(a => !a.IsDynamic && !a.FullName.StartsWith("System") && !a.FullName.StartsWith("Microsoft"));
+
+ // Register all IWorkflowActivity implementations
+ services.Scan(scan => scan
+ .FromAssemblies(assemblies)
+ .AddClasses(classes => classes.AssignableTo(typeof(IWorkflowActivity)))
+ .AsImplementedInterfaces()
+ .WithLifetime(lifetime)
+ );
+
+ // Register all IWorkflowActivities implementations
+ services.Scan(scan => scan
+ .FromAssemblies(assemblies)
+ .AddClasses(classes => classes.AssignableTo(typeof(IWorkflowActivities)))
+ .AsImplementedInterfaces()
+ .WithLifetime(lifetime)
+ );
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/Zooper.Bee/Extensions/WorkflowExtensions.cs b/Zooper.Bee/Extensions/WorkflowExtensions.cs
new file mode 100644
index 0000000..7665d9d
--- /dev/null
+++ b/Zooper.Bee/Extensions/WorkflowExtensions.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+
+namespace Zooper.Bee.Extensions;
+
+///
+/// Provides extension methods for registering all workflow components (validations, activities, and workflow classes)
+/// with dependency injection. These methods simplify the configuration process by centralizing the registration
+/// of all workflow-related services.
+///
+public static class WorkflowExtensions
+{
+ ///
+ /// Registers all workflow components from the specified assemblies into the service collection.
+ /// This includes workflow validations, workflow activities, and concrete workflow classes.
+ ///
+ /// The service collection to add the registrations to
+ /// Optional list of assemblies to scan for workflow components. If null or empty, all non-system
+ /// assemblies in the current AppDomain will be scanned
+ /// The service lifetime to use for the registered services (defaults to Scoped)
+ /// The service collection for chaining additional registrations
+ ///
+ /// This method provides a comprehensive registration of all workflow-related components:
+ /// - Workflow validations (via AddWorkflowValidations)
+ /// - Workflow activities (via AddWorkflowActivities)
+ /// - Concrete workflow classes (classes ending with "Workflow")
+ ///
+ /// Workflow classes are registered as themselves (not by interface) to support direct injection.
+ /// System and Microsoft assemblies are excluded by default when no specific assemblies are provided.
+ ///
+ public static IServiceCollection AddWorkflows(
+ this IServiceCollection services,
+ IEnumerable? assemblies = null,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ // If no assemblies are specified, use all loaded assemblies
+ var assembliesToScan = assemblies != null
+ ? assemblies.ToArray()
+ : AppDomain.CurrentDomain.GetAssemblies()
+ .Where(a => !a.IsDynamic && !a.FullName.StartsWith("System") && !a.FullName.StartsWith("Microsoft"))
+ .ToArray();
+
+ // Register all workflow validations
+ services.AddWorkflowValidations(assembliesToScan, lifetime);
+
+ // Register all workflow activities
+ services.AddWorkflowActivities(assembliesToScan, lifetime);
+
+ // Then register all classes ending with Workflow
+ services.Scan(scan => scan
+ .FromAssemblies(assembliesToScan)
+ .AddClasses(classes =>
+ classes.Where(type => type.Name.EndsWith("Workflow") && type is { IsAbstract: false, IsInterface: false })
+ )
+ .AsSelf()
+ .WithLifetime(lifetime)
+ );
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/Zooper.Bee/Extensions/WorkflowValidationExtensions.cs b/Zooper.Bee/Extensions/WorkflowValidationExtensions.cs
new file mode 100644
index 0000000..d5a57a2
--- /dev/null
+++ b/Zooper.Bee/Extensions/WorkflowValidationExtensions.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using Microsoft.Extensions.DependencyInjection;
+using Zooper.Bee.Interfaces;
+
+namespace Zooper.Bee.Extensions;
+
+///
+/// Provides extension methods for registering workflow validations with dependency injection.
+/// These methods use assembly scanning to automatically discover and register all implementations
+/// of workflow validation interfaces within specified assemblies.
+///
+public static class WorkflowValidationExtensions
+{
+ ///
+ /// Registers all workflow validations from the specified assemblies into the service collection.
+ /// This includes both individual workflow validations (IWorkflowValidation) and validation collections (IWorkflowValidations).
+ ///
+ /// The service collection to add the registrations to
+ /// Optional list of assemblies to scan for workflow validations. If null or empty, all non-system
+ /// assemblies in the current AppDomain will be scanned
+ /// The service lifetime to use for the registered services (defaults to Scoped)
+ /// The service collection for chaining additional registrations
+ ///
+ /// This method uses Scrutor to scan assemblies and register classes based on their implemented interfaces.
+ /// System and Microsoft assemblies are excluded by default when no specific assemblies are provided.
+ ///
+ public static IServiceCollection AddWorkflowValidations(
+ this IServiceCollection services,
+ IEnumerable? assemblies = null,
+ ServiceLifetime lifetime = ServiceLifetime.Scoped)
+ {
+ // If no assemblies are specified, use all loaded assemblies
+ var assembliesToScan = assemblies != null
+ ? assemblies.ToArray()
+ : AppDomain.CurrentDomain.GetAssemblies()
+ .Where(a => !a.IsDynamic && !a.FullName.StartsWith("System") && !a.FullName.StartsWith("Microsoft"))
+ .ToArray();
+
+ // Register all IWorkflowValidation implementations
+ services.Scan(scan => scan
+ .FromAssemblies(assembliesToScan)
+ .AddClasses(classes => classes.AssignableTo(typeof(IWorkflowValidation)))
+ .AsImplementedInterfaces()
+ .WithLifetime(lifetime)
+ );
+
+ // Register all IWorkflowValidations implementations
+ services.Scan(scan => scan
+ .FromAssemblies(assembliesToScan)
+ .AddClasses(classes => classes.AssignableTo(typeof(IWorkflowValidations)))
+ .AsImplementedInterfaces()
+ .WithLifetime(lifetime)
+ );
+
+ return services;
+ }
+}
\ No newline at end of file
diff --git a/Zooper.Bee/Features/IWorkflowFeature.cs b/Zooper.Bee/Features/IWorkflowFeature.cs
index b6e6a8c..8e0a657 100644
--- a/Zooper.Bee/Features/IWorkflowFeature.cs
+++ b/Zooper.Bee/Features/IWorkflowFeature.cs
@@ -7,7 +7,7 @@ namespace Zooper.Bee.Features;
///
/// The type of the main workflow payload
/// The type of the error
-public interface IWorkflowFeature
+public interface IWorkflowFeature
{
///
/// Gets the condition that determines if this feature should execute.
diff --git a/Zooper.Bee/Interfaces/IWorkflowActivities.cs b/Zooper.Bee/Interfaces/IWorkflowActivities.cs
new file mode 100644
index 0000000..ca51539
--- /dev/null
+++ b/Zooper.Bee/Interfaces/IWorkflowActivities.cs
@@ -0,0 +1,13 @@
+namespace Zooper.Bee.Interfaces;
+
+///
+/// Base marker interface for workflow activity collections
+///
+public interface IWorkflowActivities;
+
+///
+/// Interface for a collection of workflow activities operating on the same payload and error types
+///
+/// The type of payload the activities process
+/// The type of error the activities might return
+public interface IWorkflowActivities : IWorkflowActivities;
\ No newline at end of file
diff --git a/Zooper.Bee/Interfaces/IWorkflowActivity.cs b/Zooper.Bee/Interfaces/IWorkflowActivity.cs
new file mode 100644
index 0000000..8e359f9
--- /dev/null
+++ b/Zooper.Bee/Interfaces/IWorkflowActivity.cs
@@ -0,0 +1,26 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Zooper.Fox;
+
+namespace Zooper.Bee.Interfaces;
+
+///
+/// Base marker interface for all workflow activities
+///
+public interface IWorkflowActivity;
+
+///
+/// Interface for an activity that works with a specific payload and error type
+///
+/// The type of payload the activity processes
+/// The type of error the activity might return
+public interface IWorkflowActivity : IWorkflowActivity
+{
+ ///
+ /// Executes the activity with the given payload
+ ///
+ /// The input payload
+ /// Cancellation token
+ /// Either the error or the updated payload
+ Task> Execute(TPayload payload, CancellationToken cancellationToken);
+}
diff --git a/Zooper.Bee/Interfaces/IWorkflowValidation.cs b/Zooper.Bee/Interfaces/IWorkflowValidation.cs
new file mode 100644
index 0000000..502c765
--- /dev/null
+++ b/Zooper.Bee/Interfaces/IWorkflowValidation.cs
@@ -0,0 +1,28 @@
+using System.Threading;
+using System.Threading.Tasks;
+using Zooper.Fox;
+
+namespace Zooper.Bee.Interfaces;
+
+///
+/// Base marker interface for all workflow validations
+///
+public interface IWorkflowValidation;
+
+///
+/// Interface for a validation that validates a request and potentially returns an error
+///
+/// The type of request being validated
+/// The type of error that might be returned
+public interface IWorkflowValidation : IWorkflowValidation
+{
+ ///
+ /// Validates the request
+ ///
+ /// The request to validate
+ /// Cancellation token
+ /// An option containing an error if validation fails, or None if validation succeeds
+ Task