diff --git a/Cortex.sln b/Cortex.sln index 025b0b9..7efbb3f 100644 --- a/Cortex.sln +++ b/Cortex.sln @@ -57,6 +57,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Streams.Elasticsearc EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Types", "src\Cortex.Types\Cortex.Types.csproj", "{64E12D4C-FBB2-4004-8316-C886CBFC614B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cortex.Mediator.Behaviors.FluentValidation", "src\Cortex.Mediator.Behaviors.FluentValidation\Cortex.Mediator.Behaviors.FluentValidation.csproj", "{44A166BD-01E9-4A4B-9BC5-7DE01B472E73}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -170,6 +172,10 @@ Global {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Debug|Any CPU.Build.0 = Debug|Any CPU {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|Any CPU.ActiveCfg = Release|Any CPU {64E12D4C-FBB2-4004-8316-C886CBFC614B}.Release|Any CPU.Build.0 = Release|Any CPU + {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {44A166BD-01E9-4A4B-9BC5-7DE01B472E73}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/README.md b/README.md index 76df3ba..b4f342b 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,9 @@ - **Cortex.Mediator:** implementation of the Mediator pattern for .NET applications, designed to power clean, modular architectures like **Vertical Slice Architecture** and **CQRS**. [![NuGet Version](https://img.shields.io/nuget/v/Cortex.Mediator?label=Cortex.Mediator)](https://www.nuget.org/packages/Cortex.Mediator) +- **Cortex.Mediator.Behaviors.FluentValidation:** implementation of the FluentValidation validation for Commands and Queries +[![NuGet Version](https://img.shields.io/nuget/v/Cortex.Mediator.Behaviors.FluentValidation?label=Cortex.Mediator.Behaviors.FluentValidation)](https://www.nuget.org/packages/Cortex.Mediator.Behaviors.FluentValidation) + ## Getting Started diff --git a/src/Cortex.Mediator.Behaviors.FluentValidation/Assets/andyX.png b/src/Cortex.Mediator.Behaviors.FluentValidation/Assets/andyX.png new file mode 100644 index 0000000..101a1fb Binary files /dev/null and b/src/Cortex.Mediator.Behaviors.FluentValidation/Assets/andyX.png differ diff --git a/src/Cortex.Mediator.Behaviors.FluentValidation/Assets/license.md b/src/Cortex.Mediator.Behaviors.FluentValidation/Assets/license.md new file mode 100644 index 0000000..3c845d4 --- /dev/null +++ b/src/Cortex.Mediator.Behaviors.FluentValidation/Assets/license.md @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2025 Buildersoft + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/Cortex.Mediator.Behaviors.FluentValidation/Cortex.Mediator.Behaviors.FluentValidation.csproj b/src/Cortex.Mediator.Behaviors.FluentValidation/Cortex.Mediator.Behaviors.FluentValidation.csproj new file mode 100644 index 0000000..f367543 --- /dev/null +++ b/src/Cortex.Mediator.Behaviors.FluentValidation/Cortex.Mediator.Behaviors.FluentValidation.csproj @@ -0,0 +1,72 @@ + + + + net9;net8;net7;netstandard2.1 + + 1.0.0 + 1.0.0 + Buildersoft Cortex Framework + Buildersoft + 12 + Buildersoft,EnesHoxha + Copyright © Buildersoft 2025 + + + Buildersoft Cortex Mediator is a library for .NET applications that implements the mediator pattern. It helps to reduce dependencies between objects by allowing in-process messaging without direct communication. Instead, objects communicate through Cortex Mediator, making them less coupled and more maintainable.. + + + + https://github.com/buildersoftio/cortex + cortex vortex mediator eda cqrs streaming + + 1.0.0 + license.md + andyX.png + Cortex.Mediator.Behaviors.FluentValidation + True + True + True + + Just as the Cortex in our brains handles complex processing efficiently, Cortex Data Framework brings brainpower to your data management! + https://buildersoft.io/ + Cortex Mediator Behaviors FluentValidation + README.md + + + + + + + + + + True + \ + Always + + + + + True + + + + True + + + + + + + + + + + + + + + + + + diff --git a/src/Cortex.Mediator.Behaviors.FluentValidation/README.md b/src/Cortex.Mediator.Behaviors.FluentValidation/README.md new file mode 100644 index 0000000..eeb265f --- /dev/null +++ b/src/Cortex.Mediator.Behaviors.FluentValidation/README.md @@ -0,0 +1,181 @@ +# Cortex.Mediator 🧠 + +**Cortex.Mediator** is a lightweight and extensible implementation of the Mediator pattern for .NET applications, designed to power clean, modular architectures like **Vertical Slice Architecture** and **CQRS**. + + +Built as part of the [Cortex Data Framework](https://github.com/buildersoftio/cortex), this library simplifies command and query handling with built-in support for: + + +- ✅ Commands & Queries +- ✅ Notifications (Events) +- ✅ Pipeline Behaviors +- ✅ FluentValidation +- ✅ Logging + +--- + +[![GitHub License](https://img.shields.io/github/license/buildersoftio/cortex)](https://github.com/buildersoftio/cortex/blob/master/LICENSE) +[![NuGet Version](https://img.shields.io/nuget/v/Cortex.Mediator?label=Cortex.Mediator)](https://www.nuget.org/packages/Cortex.Mediator) +[![GitHub contributors](https://img.shields.io/github/contributors/buildersoftio/cortex)](https://github.com/buildersoftio/cortex) +[![Discord Shield](https://discord.com/api/guilds/1310034212371566612/widget.png?style=shield)](https://discord.gg/JnMJV33QHu) + + +## 🚀 Getting Started + +### Install via NuGet + +```bash +dotnet add package Cortex.Mediator +``` + +## 🛠️ Setup +In `Program.cs` or `Startup.cs`: +```csharp +builder.Services.AddCortexMediator( + builder.Configuration, + new[] { typeof(Program) }, // Assemblies to scan for handlers + options => options.AddDefaultBehaviors() // Logging +); +``` + +## 📦 Folder Structure Example (Vertical Slice) +```bash +Features/ + CreateUser/ + CreateUserCommand.cs + CreateUserCommandHandler.cs + CreateUserValidator.cs + CreateUserEndpoint.cs +``` + +## ✏️ Defining a Command + +```csharp +public class CreateUserCommand : ICommand +{ + public string UserName { get; set; } + public string Email { get; set; } +} +``` + +### Handler +```csharp +public class CreateUserCommandHandler : ICommandHandler +{ + public async Task Handle(CreateUserCommand command, CancellationToken cancellationToken) + { + // Logic here + } +} +``` + +### Validator (Optional, via FluentValidation) - Coming in the next release v1.8 +```csharp +public class CreateUserValidator : AbstractValidator +{ + public CreateUserValidator() + { + RuleFor(x => x.UserName).NotEmpty(); + RuleFor(x => x.Email).NotEmpty().EmailAddress(); + } +} +``` + +--- + +## 🔍 Defining a Query + +```csharp +public class GetUserQuery : IQuery +{ + public int UserId { get; set; } +} +``` +```csharp +public class GetUserQueryHandler : IQueryHandler +{ + public async Task Handle(GetUserQuery query, CancellationToken cancellationToken) + { + return new GetUserResponse { UserId = query.UserId, UserName = "Andy" }; + } +} + +``` + +## 📢 Notifications (Events) + +```csharp +public class UserCreatedNotification : INotification +{ + public string UserName { get; set; } +} + +public class SendWelcomeEmailHandler : INotificationHandler +{ + public async Task Handle(UserCreatedNotification notification, CancellationToken cancellationToken) + { + // Send email... + } +} +``` +```csharp +await mediator.PublishAsync(new UserCreatedNotification { UserName = "Andy" }); +``` + +## 🔧 Pipeline Behaviors (Built-in) +Out of the box, Cortex.Mediator supports: + +- `ValidationCommandBehavior` - Coming in the next release v1.8 +- `LoggingCommandBehavior` + +You can also register custom behaviors: +```csharp +options.AddOpenCommandPipelineBehavior(typeof(MyCustomBehavior<>)); +``` + +## 💬 Contributing +We welcome contributions from the community! Whether it's reporting bugs, suggesting features, or submitting pull requests, your involvement helps improve Cortex for everyone. + +### 💬 How to Contribute +1. **Fork the Repository** +2. **Create a Feature Branch** +```bash +git checkout -b feature/YourFeature +``` +3. **Commit Your Changes** +```bash +git commit -m "Add your feature" +``` +4. **Push to Your Fork** +```bash +git push origin feature/YourFeature +``` +5. **Open a Pull Request** + +Describe your changes and submit the pull request for review. + +## 📄 License +This project is licensed under the MIT License. + +## 📚 Sponsorship +Cortex is an open-source project maintained by BuilderSoft. Your support helps us continue developing and improving Cortex. Consider sponsoring us to contribute to the future of resilient streaming platforms. + +### How to Sponsor +* **Financial Contributions**: Support us through [GitHub Sponsors](https://github.com/sponsors/buildersoftio) or other preferred platforms. +* **Corporate Sponsorship**: If your organization is interested in sponsoring Cortex, please contact us directly. + +Contact Us: cortex@buildersoft.io + + +## Contact +We'd love to hear from you! Whether you have questions, feedback, or need support, feel free to reach out. + +- Email: support@buildersoft.io +- Website: https://buildersoft.io +- GitHub Issues: [Cortex Data Framework Issues](https://github.com/buildersoftio/cortex/issues) +- Join our Discord Community: [![Discord Shield](https://discord.com/api/guilds/1310034212371566612/widget.png?style=shield)](https://discord.gg/JnMJV33QHu) + + +Thank you for using Cortex Data Framework! We hope it empowers you to build scalable and efficient data processing pipelines effortlessly. + +Built with ❤️ by the Buildersoft team. diff --git a/src/Cortex.Mediator.Behaviors.FluentValidation/ValidationCommandBehavior.cs b/src/Cortex.Mediator.Behaviors.FluentValidation/ValidationCommandBehavior.cs new file mode 100644 index 0000000..b9eb2e3 --- /dev/null +++ b/src/Cortex.Mediator.Behaviors.FluentValidation/ValidationCommandBehavior.cs @@ -0,0 +1,43 @@ +using Cortex.Mediator.Commands; +using FluentValidation; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Cortex.Mediator.Behaviors +{ + /// + /// Pipeline behavior for validation command execution. + /// + public sealed class ValidationCommandBehavior : ICommandPipelineBehavior + where TCommand : ICommand + { + private readonly IEnumerable> _validators; + + + public async Task Handle(TCommand command, CommandHandlerDelegate next, CancellationToken cancellationToken) + { + var context = new ValidationContext(command); + var failures = _validators + .Select(v => v.Validate(context)) + .SelectMany(r => r.Errors) + .Where(f => f != null) + .ToList(); + + if (failures.Count() > 0) + { + var errors = failures + .GroupBy(f => f.PropertyName) + .ToDictionary( + g => g.Key, + g => g.Select(f => f.ErrorMessage).ToArray()); + + throw new Exceptions.ValidationException(errors); + } + + return await next(); + } + } +} diff --git a/src/Cortex.Mediator.Behaviors.FluentValidation/ValidationQueryBehavior.cs b/src/Cortex.Mediator.Behaviors.FluentValidation/ValidationQueryBehavior.cs new file mode 100644 index 0000000..b48c633 --- /dev/null +++ b/src/Cortex.Mediator.Behaviors.FluentValidation/ValidationQueryBehavior.cs @@ -0,0 +1,40 @@ +using Cortex.Mediator.Queries; +using FluentValidation; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Cortex.Mediator.Behaviors.FluentValidation +{ + public sealed class ValidationQueryBehavior : IQueryPipelineBehavior + where TQuery : IQuery + { + + private readonly IEnumerable> _validators; + + + public async Task Handle(TQuery query, QueryHandlerDelegate next, CancellationToken cancellationToken) + { + var context = new ValidationContext(query); + var failures = _validators + .Select(v => v.Validate(context)) + .SelectMany(r => r.Errors) + .Where(f => f != null) + .ToList(); + + if (failures.Count() > 0) + { + var errors = failures + .GroupBy(f => f.PropertyName) + .ToDictionary( + g => g.Key, + g => g.Select(f => f.ErrorMessage).ToArray()); + + throw new Exceptions.ValidationException(errors); + } + + return await next(); + } + } +}