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.DependencyInjection;
|
||||
using System.Reflection;
|
||||
using DigitalData.TaskFlow.Application.Common.Behaviors;
|
||||
|
||||
namespace DigitalData.TaskFlow.Application;
|
||||
|
||||
@@ -30,6 +31,7 @@ public static class DependencyInjection
|
||||
});
|
||||
|
||||
services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly);
|
||||
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
|
||||
services.AddAutoMapper(_ => { }, Assembly.GetExecutingAssembly());
|
||||
|
||||
return services;
|
||||
|
||||
Reference in New Issue
Block a user