You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Web API:Controller与Repository间服务层架构实现问询

服务层架构设计实践建议

一、服务层的位置与项目归属

  • 大型项目中建议独立为一个类库项目,命名可参考YourAppName.Services,与Web API项目、仓储层项目(如YourAppName.Repositories)平级放置。
    • 优势是职责边界清晰:Web层仅处理请求路由、响应封装;服务层聚焦业务逻辑与DTO-实体转换;仓储层专注数据持久化操作。
    • 若项目规模较小,可暂时将服务层放在仓储层项目内,但需用命名空间(如YourAppName.Repositories.Services)做隔离,后续规模扩大再拆分独立。

二、依赖注入结构

  1. 层级依赖关系:Web API项目依赖服务层,服务层依赖仓储层(含仓储接口、工作单元接口),仓储层依赖EF Core的DbContext。
  2. 注册方式:
    • 在Web项目的Program.cs中分别注册各层依赖:
      // 注册仓储与工作单元
      builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
      builder.Services.AddScoped<IProductRepository, ProductRepository>();
      // 注册服务层
      builder.Services.AddScoped<IProductService, ProductService>();
      
    • 服务层类通过构造函数注入所需的仓储与工作单元接口:
      public class ProductService : IProductService
      {
          private readonly IProductRepository _productRepo;
          private readonly IUnitOfWork _unitOfWork;
      
          public ProductService(IProductRepository productRepo, IUnitOfWork unitOfWork)
          {
              _productRepo = productRepo;
              _unitOfWork = unitOfWork;
          }
      }
      
  3. 事务控制实现:在服务层方法中完成多仓储操作后,统一调用工作单元的SaveChanges()提交事务:
    public async Task<ProductDto> CreateProductWithCategoryAsync(ProductCreateDto dto)
    {
        // DTO转实体
        var product = new Product { Name = dto.Name, Price = dto.Price };
        var category = new Category { Name = dto.CategoryName };
    
        // 调用仓储执行添加
        await _productRepo.AddAsync(product);
        await _categoryRepo.AddAsync(category);
    
        // 统一提交事务
        await _unitOfWork.SaveChangesAsync();
    
        // 实体转返回DTO
        return new ProductDto { Id = product.Id, Name = product.Name };
    }
    

三、服务层是否参与简单读取操作

  • 建议所有数据交互均通过服务层,包括简单读取。
    • 好处是保持架构一致性,避免控制器直接依赖仓储层;后续若需扩展读取逻辑(如添加缓存、权限校验、数据过滤),仅需修改服务层,无需改动控制器。
    • 示例实现:
      // 服务层读取方法
      public async Task<ProductDto> GetProductByIdAsync(int id)
      {
          var product = await _productRepo.GetByIdAsync(id);
          return new ProductDto { Id = product.Id, Name = product.Name, Price = product.Price };
      }
      
      // 控制器调用
      [HttpGet("{id}")]
      public async Task<IActionResult> GetProduct(int id)
      {
          var dto = await _productService.GetProductByIdAsync(id);
          return Ok(dto);
      }
      
  • 超大规模项目中,若为极致性能考虑,可允许控制器直接调用仓储做简单读取,但会打破架构一致性,需谨慎权衡。

四、调整后的职责划分

  • Web API控制器:仅负责接收请求、参数校验、调用服务层方法、封装响应,完全不接触实体类与数据持久化逻辑。
  • 服务层:处理所有业务逻辑(事务控制、业务规则校验)、DTO与实体的双向转换、协调多仓储操作。
  • 仓储层:仅负责数据CRUD操作,专注与数据库的交互,不包含业务逻辑,也不处理DTO转换。

内容的提问来源于stack exchange,提问作者FunkMonkey33

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 00:13:17