feat(application): add CQRS pipeline infrastructure and repository contracts
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
using FluentValidation;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace DigitalData.TaskFlow.Application.Common.Behaviors;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Runs FluentValidation validators before a MediatR request reaches its handler.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TRequest">Request type.</typeparam>
|
||||||
|
/// <typeparam name="TResponse">Response type.</typeparam>
|
||||||
|
public sealed class ValidationBehavior<TRequest, TResponse>(IEnumerable<IValidator<TRequest>> validators)
|
||||||
|
: IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Validates the request and continues the pipeline when validation succeeds.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">Current request instance.</param>
|
||||||
|
/// <param name="next">Next pipeline delegate.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>Handler response when validation passes.</returns>
|
||||||
|
/// <exception cref="ValidationException">Thrown when one or more validation errors are found.</exception>
|
||||||
|
public async Task<TResponse> Handle(
|
||||||
|
TRequest request,
|
||||||
|
RequestHandlerDelegate<TResponse> next,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (!validators.Any())
|
||||||
|
{
|
||||||
|
return await next(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
var context = new ValidationContext<TRequest>(request);
|
||||||
|
var validationResults = await Task.WhenAll(validators.Select(v => v.ValidateAsync(context, cancellationToken)));
|
||||||
|
|
||||||
|
var failures = validationResults
|
||||||
|
.SelectMany(r => r.Errors)
|
||||||
|
.Where(f => f is not null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (failures.Count != 0)
|
||||||
|
{
|
||||||
|
throw new ValidationException(failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await next(cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using DigitalData.TaskFlow.Domain.Documents;
|
||||||
|
|
||||||
|
namespace DigitalData.TaskFlow.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines document-specific data access operations.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDocumentRepository : IRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets document binary content for the given document identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="id">Document identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>Binary document payload.</returns>
|
||||||
|
Task<DocumentBinaryResult> GetAsync(string id, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace DigitalData.TaskFlow.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines a marker contract for repository abstractions in the application layer.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRepository
|
||||||
|
{
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using DigitalData.TaskFlow.Domain.Closing;
|
||||||
|
using DigitalData.TaskFlow.Domain.Workflows;
|
||||||
|
|
||||||
|
namespace DigitalData.TaskFlow.Application.Common.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines workflow-specific data access operations for reading and closing workflow items.
|
||||||
|
/// </summary>
|
||||||
|
public interface IWorkflowRepository : IRepository
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets grouped workflow data for the given WISAG user identifier.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wisagId">WISAG user identifier.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>Workflow data grouped by source.</returns>
|
||||||
|
Task<WorkflowSourcesResponse> GetAsync(int wisagId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Applies a closing decision to a workflow process.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wisagId">WISAG user identifier.</param>
|
||||||
|
/// <param name="source">Source system name.</param>
|
||||||
|
/// <param name="processId">Process identifier.</param>
|
||||||
|
/// <param name="decision">Closing decision payload.</param>
|
||||||
|
/// <param name="cancellationToken">Cancellation token.</param>
|
||||||
|
/// <returns>Closing operation result.</returns>
|
||||||
|
Task<CloseWorkflowResult> CloseAsync(
|
||||||
|
int wisagId,
|
||||||
|
string source,
|
||||||
|
int processId,
|
||||||
|
CloseWorkflowDecision decision,
|
||||||
|
CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
|
||||||
|
namespace DigitalData.TaskFlow.Application.Common.Mappings;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines AutoMapper configuration for application-level mappings.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class ApplicationMappingProfile : Profile
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes mapping configuration.
|
||||||
|
/// </summary>
|
||||||
|
public ApplicationMappingProfile()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ using MediatR;
|
|||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using DigitalData.TaskFlow.Application.Common.Behaviors;
|
||||||
|
|
||||||
namespace DigitalData.TaskFlow.Application;
|
namespace DigitalData.TaskFlow.Application;
|
||||||
|
|
||||||
@@ -30,6 +31,7 @@ public static class DependencyInjection
|
|||||||
});
|
});
|
||||||
|
|
||||||
services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly);
|
services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly);
|
||||||
|
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
|
||||||
services.AddAutoMapper(_ => { }, Assembly.GetExecutingAssembly());
|
services.AddAutoMapper(_ => { }, Assembly.GetExecutingAssembly());
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
|
|||||||
Reference in New Issue
Block a user