Skip to content
Go back

在 Clean Architecture 中平衡跨切关注点:实现高效的软件架构

Published:  at  12:00 AM

在 Clean Architecture 中平衡跨切关注点:实现高效的软件架构 🎯

在软件开发中,跨切关注点(Cross-Cutting Concerns)是影响整个应用程序的重要方面。这些功能通常贯穿于多个层级和模块,例如认证与授权、日志记录、异常处理、验证和缓存等。在这篇文章中,我们将探讨如何在 Clean Architecture 中有效地集成这些关注点,以避免代码重复和组件间的紧密耦合。

什么是跨切关注点?

跨切关注点指的是那些需要贯穿于应用程序多个部分的功能。常见的包括:

在 Clean Architecture 中,处理跨切关注点对于确保系统的可维护性和扩展性至关重要。理想情况下,这些关注点应该与核心业务逻辑分开处理,以保持架构的清晰和适应性。

实现跨切关注点的方法

我们可以在基础设施层中实现跨切关注点,使用 ASP.NET Core 中间件、装饰器模式或 MediatR 管道行为等工具来管理这些关注点。

日志记录 📊

日志记录是软件开发中的基本功能,帮助开发者了解应用程序的行为。在 Clean Architecture 中,日志记录应该保持关注点分离。一个优雅的方法是使用 MediatR 的 IPipelineBehavior 来封装日志逻辑,使其成为独立的关注点。

using Serilog.Context;

internal sealed class RequestLoggingPipelineBehavior<TRequest, TResponse>(
    ILogger<RequestLoggingPipelineBehavior<TRequest, TResponse>> logger)
    : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        string requestName = typeof(TRequest).Name;
        logger.LogInformation("Processing request {RequestName}", requestName);

        TResponse result = await next();

        if (result.IsSuccess)
        {
            logger.LogInformation("Completed request {RequestName}", requestName);
        }
        else
        {
            using (LogContext.PushProperty("Error", result.Error, true))
            {
                logger.LogError("Completed request {RequestName} with error", requestName);
            }
        }

        return result;
    }
}

数据验证 🛡️

验证是保护系统免受错误数据侵入的关键措施。通过创建验证管道行为,可以将验证逻辑与业务逻辑分离,确保每个请求在进入核心处理逻辑之前都得到验证。

internal sealed class ValidationPipelineBehavior<TRequest, TResponse>(
    IEnumerable<IValidator<TRequest>> validators)
    : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        ValidationFailure[] validationFailures = await ValidateAsync(request);

        if (validationFailures.Length != 0)
        {
            throw new ValidationException(validationFailures);
        }

        return await next();
    }

    private async Task<ValidationFailure[]> ValidateAsync(TRequest request)
    {
        if (!validators.Any())
        {
            return [];
        }

        var context = new ValidationContext<TRequest>(request);

        ValidationResult[] validationResults = await Task.WhenAll(
            validators.Select(validator => validator.ValidateAsync(context)));

        ValidationFailure[] validationFailures = validationResults
            .Where(validationResult => !validationResult.IsValid)
            .SelectMany(validationResult => validationResult.Errors)
            .ToArray();

        return validationFailures;
    }
}

缓存 🚀

缓存旨在提高性能和可扩展性,通过快速访问层暂时存储数据,以减少重复获取或计算相同信息的需求。我们可以使用 Cache Aside 模式来实现缓存。

internal sealed class QueryCachingPipelineBehavior<TRequest, TResponse>(
    ICacheService cacheService,
    ILogger<QueryCachingPipelineBehavior<TRequest, TResponse>> logger)
    : IPipelineBehavior<TRequest, TResponse>
{
    public async Task<TResponse> Handle(
        TRequest request,
        RequestHandlerDelegate<TResponse> next,
        CancellationToken cancellationToken)
    {
        TResponse? cachedResult = await cacheService.GetAsync<TResponse>(
            request.CacheKey,
            cancellationToken);

        string requestName = typeof(TRequest).Name;
        if (cachedResult is not null)
        {
            logger.LogInformation("Cache hit for {RequestName}", requestName);
            return cachedResult;
        }

        logger.LogInformation("Cache miss for {RequestName}", requestName);

        TResponse result = await next();

        if (result.IsSuccess)
        {
            await cacheService.SetAsync(
                request.CacheKey,
                result,
                request.Expiration,
                cancellationToken);
        }

        return result;
    }
}

接下来做什么? 🔍

管理跨切关注点如日志记录、缓存、验证和异常处理不仅仅是技术实现的问题,更是与 Clean Architecture 核心原则对齐的问题。通过采用这些解耦技术,可以确保您的 .NET 项目更加稳健和易于维护。



Previous Post
使用 MediatR 的 CQRS 模式
Next Post
如何理解单点登录(SSO)工作原理?一文讲透!