如何扩展ASP.NET Core 6 Clean Architecture模板:添加新功能模块的完整指南
如何扩展ASP.NET Core 6 Clean Architecture模板添加新功能模块的完整指南【免费下载链接】CleanArchitectureASP.NET Core 6 Web API Clean Architecture Solution Template项目地址: https://gitcode.com/gh_mirrors/cleanarchitecture/CleanArchitectureASP.NET Core 6 Clean Architecture模板是一个强大的企业级应用开发框架遵循领域驱动设计DDD原则。本文将为您提供完整的扩展指南帮助您快速添加新功能模块到这个架构中。通过本教程您将学会如何从零开始创建新的业务模块并保持代码的整洁和可维护性。 理解Clean Architecture核心层次在开始扩展之前让我们先了解ASP.NET Core 6 Clean Architecture模板的四个核心层次1. 领域层Domain Layer这是架构的核心包含所有业务实体、值对象、领域事件和业务规则。领域层完全独立于其他层确保业务逻辑的纯粹性。2. 应用层Application Layer应用层包含所有用例逻辑它协调领域对象来完成特定的业务用例。这一层定义了接口由基础设施层实现。3. 基础设施层Infrastructure Layer基础设施层提供外部资源的访问实现如数据库访问、文件系统操作、外部API调用等。4. 表现层Presentation Layer通常是Web API或UI层负责接收用户请求并返回响应。 添加新功能模块的完整步骤步骤1定义领域实体首先在领域层创建您的业务实体。以添加产品模块为例// src/Common/CleanArchitecture.Domain/Entities/Product.cs namespace CleanArchitecture.Domain.Entities { public class Product : AuditableEntity, IHasDomainEvent { public Product() { DomainEvents new ListDomainEvent(); } public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public int StockQuantity { get; set; } public bool IsActive { get; set; } public ListDomainEvent DomainEvents { get; set; } } }步骤2创建数据传输对象DTO在应用层定义数据传输对象用于在不同层之间传递数据// src/Common/CleanArchitecture.Application/Dto/ProductDto.cs namespace CleanArchitecture.Application.Dto { public class ProductDto : IRegister { public int Id { get; set; } public string Name { get; set; } public string Description { get; set; } public decimal Price { get; set; } public int StockQuantity { get; set; } public bool IsActive { get; set; } public string CreateDate { get; set; } public void Register(TypeAdapterConfig config) { config.NewConfigProduct, ProductDto() .Map(dest dest.CreateDate, src ${src.CreateDate.ToShortDateString()}); } } }步骤3实现应用层命令和查询使用CQRS模式为产品模块创建命令和查询创建产品命令// src/Common/CleanArchitecture.Application/Products/Commands/Create/CreateProductCommand.cs public record CreateProductCommand(string Name, string Description, decimal Price, int StockQuantity) : IRequestWrapperProductDto; public class CreateProductCommandHandler : IRequestHandlerWrapperCreateProductCommand, ProductDto { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; public CreateProductCommandHandler(IApplicationDbContext context, IMapper mapper) { _context context; _mapper mapper; } public async TaskServiceResultProductDto Handle( CreateProductCommand request, CancellationToken cancellationToken) { var entity new Product { Name request.Name, Description request.Description, Price request.Price, StockQuantity request.StockQuantity, IsActive true }; entity.DomainEvents.Add(new ProductCreatedEvent(entity)); await _context.Products.AddAsync(entity, cancellationToken); await _context.SaveChangesAsync(cancellationToken); return ServiceResult.Success(_mapper.MapProductDto(entity)); } }查询产品列表// src/Common/CleanArchitecture.Application/Products/Queries/GetProducts/GetAllProductsQuery.cs public class GetAllProductsQuery : IRequestWrapperListProductDto { } public class GetAllProductsQueryHandler : IRequestHandlerWrapperGetAllProductsQuery, ListProductDto { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; public GetAllProductsQueryHandler(IApplicationDbContext context, IMapper mapper) { _context context; _mapper mapper; } public async TaskServiceResultListProductDto Handle( GetAllProductsQuery request, CancellationToken cancellationToken) { var products await _context.Products .ProjectToTypeProductDto() .ToListAsync(cancellationToken); return ServiceResult.Success(products); } }步骤4添加验证器使用FluentValidation为命令添加验证逻辑// src/Common/CleanArchitecture.Application/Products/Commands/Create/CreateProductCommandValidator.cs public class CreateProductCommandValidator : AbstractValidatorCreateProductCommand { public CreateProductCommandValidator() { RuleFor(x x.Name) .NotEmpty().WithMessage(产品名称不能为空) .MaximumLength(200).WithMessage(产品名称不能超过200个字符); RuleFor(x x.Price) .GreaterThan(0).WithMessage(价格必须大于0); RuleFor(x x.StockQuantity) .GreaterThanOrEqualTo(0).WithMessage(库存数量不能为负数); } }步骤5创建Web API控制器在表现层添加控制器来处理HTTP请求// src/Apps/CleanArchitecture.Api/Controllers/ProductsController.cs namespace CleanArchitecture.Api.Controllers { [Authorize] public class ProductsController : BaseApiController { [HttpPost] public async TaskActionResultServiceResultProductDto Create(CreateProductCommand command) { return Ok(await Mediator.Send(command)); } [HttpGet] public async TaskActionResultServiceResultListProductDto GetAll() { return Ok(await Mediator.Send(new GetAllProductsQuery())); } [HttpGet({id})] public async TaskActionResultServiceResultProductDto GetById(int id) { return Ok(await Mediator.Send(new GetProductByIdQuery { Id id })); } [HttpPut({id})] public async TaskActionResultServiceResultProductDto Update( int id, UpdateProductCommand command) { if (id ! command.Id) return BadRequest(); return Ok(await Mediator.Send(command)); } [HttpDelete({id})] public async TaskActionResultServiceResultint Delete(int id) { return Ok(await Mediator.Send(new DeleteProductCommand { Id id })); } } }步骤6配置数据库上下文在基础设施层更新数据库上下文添加新的DbSet// src/Common/CleanArchitecture.Infrastructure/Persistence/ApplicationDbContext.cs public class ApplicationDbContext : DbContext, IApplicationDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } public DbSetProduct Products { get; set; } // ... 其他DbSet protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()); base.OnModelCreating(modelBuilder); } }步骤7创建数据库迁移根据您选择的数据库提供程序运行相应的迁移命令# 对于SQLite dotnet ef migrations add AddProductsTable \ --project src/Common/CleanArchitecture.Infrastructure.Sqlite \ --startup-project src/Apps/CleanArchitecture.Api # 对于SQL Server dotnet ef migrations add AddProductsTable \ --project src/Common/CleanArchitecture.Infrastructure.SqlServer \ --startup-project src/Apps/CleanArchitecture.Api # 应用迁移 dotnet ef database update \ --project src/Common/CleanArchitecture.Infrastructure.SqlServer \ --startup-project src/Apps/CleanArchitecture.Api步骤8配置依赖注入在应用层的DependencyInjection.cs中添加新模块的依赖注入配置// src/Common/CleanArchitecture.Application/DependencyInjection.cs public static class DependencyInjection { public static IServiceCollection AddApplication(this IServiceCollection services) { services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly()); services.AddMediatR(Assembly.GetExecutingAssembly()); services.AddTransient(typeof(IPipelineBehavior,), typeof(ValidationBehaviour,)); // 添加产品相关的服务 services.AddScopedIRequestHandlerCreateProductCommand, ServiceResultProductDto, CreateProductCommandHandler(); services.AddScopedIRequestHandlerGetAllProductsQuery, ServiceResultListProductDto, GetAllProductsQueryHandler(); return services; } } 高级扩展技巧1. 添加领域事件处理实现领域事件来处理业务逻辑的副作用// src/Common/CleanArchitecture.Application/Products/EventHandler/ProductCreatedEventHandler.cs public class ProductCreatedEventHandler : INotificationHandlerDomainEventNotificationProductCreatedEvent { private readonly ILoggerProductCreatedEventHandler _logger; public ProductCreatedEventHandler(ILoggerProductCreatedEventHandler logger) { _logger logger; } public Task Handle( DomainEventNotificationProductCreatedEvent notification, CancellationToken cancellationToken) { var domainEvent notification.DomainEvent; _logger.LogInformation(产品创建事件处理: {ProductId} - {ProductName}, domainEvent.Product.Id, domainEvent.Product.Name); // 这里可以添加其他业务逻辑如发送邮件通知、更新缓存等 return Task.CompletedTask; } }2. 实现分页查询扩展查询功能支持分页和过滤// src/Common/CleanArchitecture.Application/Products/Queries/GetProductsWithPagination/GetProductsWithPaginationQuery.cs public class GetProductsWithPaginationQuery : IRequestWrapperPaginatedListProductDto { public int PageNumber { get; set; } 1; public int PageSize { get; set; } 10; public string? SearchTerm { get; set; } public decimal? MinPrice { get; set; } public decimal? MaxPrice { get; set; } } public class GetProductsWithPaginationQueryHandler : IRequestHandlerWrapperGetProductsWithPaginationQuery, PaginatedListProductDto { private readonly IApplicationDbContext _context; private readonly IMapper _mapper; public GetProductsWithPaginationQueryHandler(IApplicationDbContext context, IMapper mapper) { _context context; _mapper mapper; } public async TaskServiceResultPaginatedListProductDto Handle( GetProductsWithPaginationQuery request, CancellationToken cancellationToken) { var query _context.Products.AsQueryable(); // 应用过滤条件 if (!string.IsNullOrEmpty(request.SearchTerm)) { query query.Where(p p.Name.Contains(request.SearchTerm) || p.Description.Contains(request.SearchTerm)); } if (request.MinPrice.HasValue) { query query.Where(p p.Price request.MinPrice.Value); } if (request.MaxPrice.HasValue) { query query.Where(p p.Price request.MaxPrice.Value); } // 应用分页 return ServiceResult.Success(await query .ProjectToTypeProductDto() .PaginatedListAsync(request.PageNumber, request.PageSize)); } }3. 添加缓存支持在基础设施层实现缓存机制// src/Common/CleanArchitecture.Infrastructure/Services/CacheService.cs public class CacheService : ICacheService { private readonly IDistributedCache _cache; public CacheService(IDistributedCache cache) { _cache cache; } public async TaskT? GetAsyncT(string key, CancellationToken cancellationToken default) { var bytes await _cache.GetAsync(key, cancellationToken); if (bytes null) return default; return JsonSerializer.DeserializeT(bytes); } public async Task SetAsyncT(string key, T value, TimeSpan? expiration null, CancellationToken cancellationToken default) { var bytes JsonSerializer.SerializeToUtf8Bytes(value); var options new DistributedCacheEntryOptions(); if (expiration.HasValue) { options.SetAbsoluteExpiration(expiration.Value); } await _cache.SetAsync(key, bytes, options, cancellationToken); } } 最佳实践建议1. 保持单一职责原则每个类和方法都应该只有一个明确的责任。命令处理器只处理命令逻辑查询处理器只处理查询逻辑。2. 使用依赖注入充分利用ASP.NET Core的依赖注入系统确保代码的可测试性和松耦合。3. 实现适当的异常处理在应用层使用统一的异常处理机制确保API返回一致的错误响应。4. 编写单元测试为每个功能模块编写全面的单元测试确保代码质量// tests/Application.UnitTests/Products/Commands/CreateProductCommandTests.cs public class CreateProductCommandTests { [Test] public async Task ShouldCreateProduct() { // 准备 var context ApplicationDbContextFactory.Create(); var mapper new MapperConfiguration(cfg cfg.AddProfileMappingProfile()).CreateMapper(); var handler new CreateProductCommandHandler(context, mapper); var command new CreateProductCommand( 测试产品, 产品描述, 99.99m, 100); // 执行 var result await handler.Handle(command, CancellationToken.None); // 断言 result.Succeeded.Should().BeTrue(); result.Data.Should().NotBeNull(); result.Data.Name.Should().Be(测试产品); var product await context.Products.FindAsync(result.Data.Id); product.Should().NotBeNull(); product.Name.Should().Be(测试产品); } }5. 使用Swagger文档确保API端点有完整的Swagger文档// 在控制器方法上添加Swagger注释 [HttpPost] [ProducesResponseType(typeof(ProductDto), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)] [SwaggerOperation( Summary 创建新产品, Description 创建一个新的产品记录, OperationId CreateProduct)] public async TaskActionResultServiceResultProductDto Create(CreateProductCommand command) { return Ok(await Mediator.Send(command)); } 模块扩展检查清单在完成新功能模块扩展后使用以下检查清单确保所有步骤都已完成✅ 在领域层创建了实体类 ✅ 在应用层创建了DTO对象 ✅ 实现了命令和查询处理器 ✅ 添加了验证逻辑 ✅ 创建了Web API控制器 ✅ 更新了数据库上下文 ✅ 创建了数据库迁移 ✅ 配置了依赖注入 ✅ 添加了Swagger文档 ✅ 编写了单元测试 ✅ 更新了API文档 总结通过本指南您已经学会了如何在ASP.NET Core 6 Clean Architecture模板中扩展新功能模块。这个模板的强大之处在于其清晰的层次分离和遵循领域驱动设计原则使得添加新功能变得简单而有序。记住Clean Architecture的核心思想是依赖关系指向内部高层模块不依赖于低层模块。通过遵循这种架构模式您的应用程序将变得更加可测试、可维护和可扩展。现在您可以自信地开始扩展您的ASP.NET Core 6 Clean Architecture应用程序添加更多业务功能同时保持代码的整洁和架构的完整性。祝您编码愉快 【免费下载链接】CleanArchitectureASP.NET Core 6 Web API Clean Architecture Solution Template项目地址: https://gitcode.com/gh_mirrors/cleanarchitecture/CleanArchitecture创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考