找回密码
 立即注册
首页 业界区 安全 第六章 应用层与API设计

第六章 应用层与API设计

于映雪 2026-1-22 23:05:03
第六章 应用层与API设计

应用层是DDD架构中的"导演",它不演戏(业务逻辑),但负责协调所有演员(领域对象)来完成一场精彩的表演(业务用例)。这一层设计得好坏,直接影响整个系统的可维护性和扩展性。
6.1 应用服务设计

6.1.1 应用服务的真正角色

很多团队把应用服务写成了"事务脚本",里面塞满了if-else和业务逻辑。这不是DDD的精神。应用服务应该是优雅的协调者,就像交响乐队的指挥。
应用服务的核心职责
让我用一个电商下单的例子来说明:
  1. // 应用服务接口 - 定义业务用例
  2. public interface IOrderApplicationService
  3. {
  4.     Task<PlaceOrderResult> PlaceOrder(PlaceOrderCommand command);
  5.     Task<ConfirmOrderResult> ConfirmOrder(ConfirmOrderCommand command);
  6.     Task<CancelOrderResult> CancelOrder(CancelOrderCommand command);
  7.     Task<ShipOrderResult> ShipOrder(ShipOrderCommand command);
  8. }
  9. // 应用服务实现 - 协调领域对象
  10. public class OrderApplicationService : IOrderApplicationService
  11. {
  12.     private readonly ICustomerRepository _customerRepository;
  13.     private readonly IProductRepository _productRepository;
  14.     private readonly IOrderRepository _orderRepository;
  15.     private readonly IInventoryService _inventoryService;
  16.     private readonly IPaymentService _paymentService;
  17.     private readonly IUnitOfWork _unitOfWork;
  18.     private readonly IDomainEventPublisher _domainEventPublisher;
  19.     private readonly ILogger<OrderApplicationService> _logger;
  20.    
  21.     public OrderApplicationService(
  22.         ICustomerRepository customerRepository,
  23.         IProductRepository productRepository,
  24.         IOrderRepository orderRepository,
  25.         IInventoryService inventoryService,
  26.         IPaymentService paymentService,
  27.         IUnitOfWork unitOfWork,
  28.         IDomainEventPublisher domainEventPublisher,
  29.         ILogger<OrderApplicationService> logger)
  30.     {
  31.         _customerRepository = customerRepository;
  32.         _productRepository = productRepository;
  33.         _orderRepository = orderRepository;
  34.         _inventoryService = inventoryService;
  35.         _paymentService = paymentService;
  36.         _unitOfWork = unitOfWork;
  37.         _domainEventPublisher = domainEventPublisher;
  38.         _logger = logger;
  39.     }
  40.    
  41.     public async Task<PlaceOrderResult> PlaceOrder(PlaceOrderCommand command)
  42.     {
  43.         _logger.LogInformation("Placing order for customer {CustomerId}", command.CustomerId);
  44.         
  45.         try
  46.         {
  47.             // 1. 获取领域对象
  48.             var customer = await _customerRepository.GetByIdAsync(new CustomerId(command.CustomerId));
  49.             if (customer == null)
  50.                 return PlaceOrderResult.Fail($"Customer {command.CustomerId} not found");
  51.             
  52.             // 2. 验证商品库存
  53.             var orderItems = new List<OrderItem>();
  54.             foreach (var item in command.Items)
  55.             {
  56.                 var product = await _productRepository.GetByIdAsync(new ProductId(item.ProductId));
  57.                 if (product == null)
  58.                     return PlaceOrderResult.Fail($"Product {item.ProductId} not found");
  59.                     
  60.                 var stockAvailable = await _inventoryService.CheckStockAsync(product.Id, item.Quantity);
  61.                 if (!stockAvailable)
  62.                     return PlaceOrderResult.Fail($"Insufficient stock for product {product.Name}");
  63.                     
  64.                 orderItems.Add(new OrderItem(product.Id, product.Name, product.Price, item.Quantity));
  65.             }
  66.             
  67.             // 3. 执行业务操作 - 委托给领域对象
  68.             var order = customer.PlaceOrder(orderItems);
  69.             
  70.             // 4. 预留库存
  71.             foreach (var item in order.Items)
  72.             {
  73.                 await _inventoryService.ReserveStockAsync(item.ProductId, item.Quantity);
  74.             }
  75.             
  76.             // 5. 持久化
  77.             await _orderRepository.AddAsync(order);
  78.             await _unitOfWork.CommitAsync();
  79.             
  80.             // 6. 发布领域事件
  81.             await _domainEventPublisher.PublishEventsAsync(order);
  82.             
  83.             _logger.LogInformation("Order placed successfully: {OrderId}", order.Id.Value);
  84.             
  85.             return PlaceOrderResult.Success(order.Id, order.TotalPrice);
  86.         }
  87.         catch (Exception ex)
  88.         {
  89.             _logger.LogError(ex, "Error placing order for customer {CustomerId}", command.CustomerId);
  90.             return PlaceOrderResult.Fail("An error occurred while placing the order");
  91.         }
  92.     }
  93.    
  94.     public async Task<ConfirmOrderResult> ConfirmOrder(ConfirmOrderCommand command)
  95.     {
  96.         var order = await _orderRepository.GetByIdAsync(new OrderId(command.OrderId));
  97.         if (order == null)
  98.             return ConfirmOrderResult.Fail($"Order {command.OrderId} not found");
  99.             
  100.         // 委托给领域对象
  101.         order.Confirm();
  102.         
  103.         // 处理支付
  104.         var paymentResult = await _paymentService.ProcessPayment(order.CustomerId, order.TotalPrice);
  105.         if (!paymentResult.Success)
  106.         {
  107.             return ConfirmOrderResult.Fail($"Payment failed: {paymentResult.ErrorMessage}");
  108.         }
  109.         
  110.         await _unitOfWork.CommitAsync();
  111.         await _domainEventPublisher.PublishEventsAsync(order);
  112.         
  113.         return ConfirmOrderResult.Success(order.Id);
  114.     }
  115. }
复制代码
6.1.2 CQRS模式的应用

CQRS(命令查询职责分离)是应用层设计的利器。它把读写操作分开,让系统更加清晰和高效。
  1. // 命令 - 改变系统状态
  2. public record CreateProductCommand(
  3.     string Name,
  4.     string Description,
  5.     decimal Price,
  6.     string Currency,
  7.     Guid CategoryId,
  8.     List<string> ImageUrls
  9. ) : IRequest<Result<ProductDto>>;
  10. public record UpdateProductPriceCommand(
  11.     Guid ProductId,
  12.     decimal NewPrice,
  13.     string Currency
  14. ) : IRequest<Result>;
  15. // 查询 - 获取系统状态
  16. public record GetProductByIdQuery(Guid ProductId) : IRequest<Result<ProductDto>>;
  17. public record GetProductsByCategoryQuery(Guid CategoryId, int Page, int PageSize)
  18.     : IRequest<Result<PagedResult<ProductDto>>>;
  19. // 命令处理器
  20. public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, Result<ProductDto>>
  21. {
  22.     private readonly IProductRepository _repository;
  23.     private readonly IUnitOfWork _unitOfWork;
  24.     private readonly IMapper _mapper;
  25.    
  26.     public async Task<Result<ProductDto>> Handle(CreateProductCommand request, CancellationToken cancellationToken)
  27.     {
  28.         var product = Product.Create(
  29.             request.Name,
  30.             request.Description,
  31.             Money.Create(request.Price, request.Currency),
  32.             new CategoryId(request.CategoryId)
  33.         );
  34.         
  35.         foreach (var imageUrl in request.ImageUrls)
  36.         {
  37.             product.AddImage(imageUrl);
  38.         }
  39.         
  40.         await _repository.AddAsync(product);
  41.         await _unitOfWork.CommitAsync();
  42.         
  43.         return Result.Success(_mapper.Map<ProductDto>(product));
  44.     }
  45. }
  46. // 查询处理器
  47. public class GetProductByIdQueryHandler : IRequestHandler<GetProductByIdQuery, Result<ProductDto>>
  48. {
  49.     private readonly IProductRepository _repository;
  50.     private readonly IMapper _mapper;
  51.    
  52.     public async Task<Result<ProductDto>> Handle(GetProductByIdQuery request, CancellationToken cancellationToken)
  53.     {
  54.         var product = await _repository.GetByIdAsync(new ProductId(request.ProductId));
  55.         if (product == null)
  56.             return Result.Failure<ProductDto>("Product not found");
  57.             
  58.         return Result.Success(_mapper.Map<ProductDto>(product));
  59.     }
  60. }
复制代码
6.1.3 结果模式:优雅的错误处理

传统的异常处理方式在分布式系统中会带来很多问题。结果模式是一个更好的选择。
  1. // 结果模式基类
  2. public class Result
  3. {
  4.     public bool IsSuccess { get; }
  5.     public bool IsFailure => !IsSuccess;
  6.     public string Error { get; }
  7.    
  8.     protected Result(bool isSuccess, string error)
  9.     {
  10.         IsSuccess = isSuccess;
  11.         Error = error;
  12.     }
  13.    
  14.     public static Result Success() => new Result(true, string.Empty);
  15.     public static Result Failure(string error) => new Result(false, error);
  16.    
  17.     public static Result<T> Success<T>(T value) => new Result<T>(value, true, string.Empty);
  18.     public static Result<T> Failure<T>(string error) => new Result<T>(default, false, error);
  19. }
  20. public class Result<T> : Result
  21. {
  22.     public T Value { get; }
  23.    
  24.     protected internal Result(T value, bool isSuccess, string error)
  25.         : base(isSuccess, error)
  26.     {
  27.         Value = value;
  28.     }
  29.    
  30.     public TResult Match<TResult>(Func<T, TResult> onSuccess, Func<string, TResult> onFailure)
  31.     {
  32.         return IsSuccess ? onSuccess(Value) : onFailure(Error);
  33.     }
  34. }
  35. // 在控制器中使用
  36. [ApiController]
  37. [Route("api/v1/[controller]")]
  38. public class OrdersController : ControllerBase
  39. {
  40.     private readonly IMediator _mediator;
  41.    
  42.     [HttpPost]
  43.     public async Task> CreateOrder(CreateOrderCommand command)
  44.     {
  45.         var result = await _mediator.Send(command);
  46.         
  47.         return result.Match>(
  48.             order => CreatedAtAction(
  49.                 nameof(GetOrder),
  50.                 new { id = order.Id },
  51.                 order),
  52.             error => BadRequest(new { Error = error }));
  53.     }
  54.    
  55.     [HttpGet("{id}")]
  56.     public async Task> GetOrder(Guid id)
  57.     {
  58.         var result = await _mediator.Send(new GetOrderByIdQuery(id));
  59.         
  60.         return result.Match>(
  61.             order => Ok(order),
  62.             error => NotFound(new { Error = error }));
  63.     }
  64. }
复制代码
6.2 API设计最佳实践

6.2.1 RESTful API设计
  1. // 资源路由设计
  2. [ApiController]
  3. [Route("api/v1/[controller]")]
  4. [Produces("application/json")]
  5. public class ProductsController : ControllerBase
  6. {
  7.     // GET api/v1/products?page=1&size=20&category=electronics
  8.     [HttpGet]
  9.     [ProducesResponseType(typeof(PagedResult<ProductDto>), StatusCodes.Status200OK)]
  10.     public async Task>> GetProducts(
  11.         [FromQuery] GetProductsQuery query)
  12.     {
  13.         var result = await _mediator.Send(query);
  14.         return Ok(result);
  15.     }
  16.    
  17.     // GET api/v1/products/{id}
  18.     [HttpGet("{id:guid}")]
  19.     [ProducesResponseType(typeof(ProductDto), StatusCodes.Status200OK)]
  20.     [ProducesResponseType(StatusCodes.Status404NotFound)]
  21.     public async Task> GetProduct(Guid id)
  22.     {
  23.         var result = await _mediator.Send(new GetProductByIdQuery(id));
  24.         
  25.         return result.Match>(
  26.             product => Ok(product),
  27.             error => NotFound());
  28.     }
  29.    
  30.     // POST api/v1/products
  31.     [HttpPost]
  32.     [Authorize(Roles = "Admin")]
  33.     [ProducesResponseType(typeof(ProductDto), StatusCodes.Status201Created)]
  34.     [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
  35.     public async Task> CreateProduct(
  36.         [FromBody] CreateProductCommand command)
  37.     {
  38.         var result = await _mediator.Send(command);
  39.         
  40.         return result.Match>(
  41.             product => CreatedAtAction(
  42.                 nameof(GetProduct),
  43.                 new { id = product.Id },
  44.                 product),
  45.             error => BadRequest(new { Error = error }));
  46.     }
  47.    
  48.     // PUT api/v1/products/{id}
  49.     [HttpPut("{id:guid}")]
  50.     [Authorize(Roles = "Admin")]
  51.     [ProducesResponseType(StatusCodes.Status204NoContent)]
  52.     [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
  53.     public async Task UpdateProduct(
  54.         Guid id,
  55.         [FromBody] UpdateProductCommand command)
  56.     {
  57.         if (id != command.ProductId)
  58.             return BadRequest("ID mismatch");
  59.             
  60.         var result = await _mediator.Send(command);
  61.         
  62.         return result.Match(
  63.             _ => NoContent(),
  64.             error => BadRequest(new { Error = error }));
  65.     }
  66.    
  67.     // DELETE api/v1/products/{id}
  68.     [HttpDelete("{id:guid}")]
  69.     [Authorize(Roles = "Admin")]
  70.     [ProducesResponseType(StatusCodes.Status204NoContent)]
  71.     [ProducesResponseType(StatusCodes.Status404NotFound)]
  72.     public async Task DeleteProduct(Guid id)
  73.     {
  74.         var result = await _mediator.Send(new DeleteProductCommand(id));
  75.         
  76.         return result.Match(
  77.             _ => NoContent(),
  78.             error => NotFound(new { Error = error }));
  79.     }
  80.    
  81.     // 子资源 - GET api/v1/products/{id}/reviews
  82.     [HttpGet("{id:guid}/reviews")]
  83.     [ProducesResponseType(typeof(List<ProductReviewDto>), StatusCodes.Status200OK)]
  84.     public async Task>> GetProductReviews(Guid id)
  85.     {
  86.         var result = await _mediator.Send(new GetProductReviewsQuery(id));
  87.         return Ok(result);
  88.     }
  89. }
复制代码
6.2.2 API版本控制策略
  1. // 方式1:URL版本控制
  2. [ApiVersion("1.0")]
  3. [Route("api/v{version:apiVersion}/[controller]")]
  4. public class ProductsController : ControllerBase
  5. {
  6.     [HttpGet("{id}")]
  7.     public async Task> GetProduct(Guid id)
  8.     {
  9.         // v1版本的实现
  10.     }
  11. }
  12. [ApiVersion("2.0")]
  13. [Route("api/v{version:apiVersion}/[controller]")]
  14. public class ProductsV2Controller : ControllerBase
  15. {
  16.     [HttpGet("{id}")]
  17.     public async Task> GetProduct(Guid id)
  18.     {
  19.         // v2版本的实现 - 可能包含更多字段
  20.     }
  21. }
  22. // 方式2:媒体类型版本控制
  23. [ApiController]
  24. [Route("api/[controller]")]
  25. public class ProductsController : ControllerBase
  26. {
  27.     [HttpGet("{id}")]
  28.     [Produces("application/vnd.myapi.product.v1+json")]
  29.     public async Task> GetProductV1(Guid id)
  30.     {
  31.         // v1版本
  32.     }
  33.    
  34.     [HttpGet("{id}")]
  35.     [Produces("application/vnd.myapi.product.v2+json")]
  36.     public async Task> GetProductV2(Guid id)
  37.     {
  38.         // v2版本
  39.     }
  40. }
复制代码
6.2.3 错误处理标准化
  1. // 统一的错误响应格式
  2. public class ApiErrorResponse
  3. {
  4.     public string Type { get; set; }
  5.     public string Title { get; set; }
  6.     public int Status { get; set; }
  7.     public string Detail { get; set; }
  8.     public string Instance { get; set; }
  9.     public Dictionary<string, string[]> Errors { get; set; }
  10.    
  11.     public static ApiErrorResponse FromException(Exception exception, HttpContext context)
  12.     {
  13.         return exception switch
  14.         {
  15.             ValidationException validationEx => new ApiErrorResponse
  16.             {
  17.                 Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  18.                 Title = "Validation Error",
  19.                 Status = StatusCodes.Status400BadRequest,
  20.                 Detail = "One or more validation errors occurred.",
  21.                 Instance = context.Request.Path,
  22.                 Errors = validationEx.Errors
  23.             },
  24.             NotFoundException notFoundEx => new ApiErrorResponse
  25.             {
  26.                 Type = "https://tools.ietf.org/html/rfc7231#section-6.5.4",
  27.                 Title = "Resource Not Found",
  28.                 Status = StatusCodes.Status404NotFound,
  29.                 Detail = notFoundEx.Message,
  30.                 Instance = context.Request.Path
  31.             },
  32.             BusinessRuleException businessEx => new ApiErrorResponse
  33.             {
  34.                 Type = "https://tools.ietf.org/html/rfc7231#section-6.5.8",
  35.                 Title = "Business Rule Violation",
  36.                 Status = StatusCodes.Status422UnprocessableEntity,
  37.                 Detail = businessEx.Message,
  38.                 Instance = context.Request.Path
  39.             },
  40.             _ => new ApiErrorResponse
  41.             {
  42.                 Type = "https://tools.ietf.org/html/rfc7231#section-6.6.1",
  43.                 Title = "Internal Server Error",
  44.                 Status = StatusCodes.Status500InternalServerError,
  45.                 Detail = "An unexpected error occurred.",
  46.                 Instance = context.Request.Path
  47.             }
  48.         };
  49.     }
  50. }
  51. // 全局异常处理中间件
  52. public class ErrorHandlingMiddleware
  53. {
  54.     private readonly RequestDelegate _next;
  55.     private readonly ILogger<ErrorHandlingMiddleware> _logger;
  56.    
  57.     public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger)
  58.     {
  59.         _next = next;
  60.         _logger = logger;
  61.     }
  62.    
  63.     public async Task InvokeAsync(HttpContext context)
  64.     {
  65.         try
  66.         {
  67.             await _next(context);
  68.         }
  69.         catch (Exception ex)
  70.         {
  71.             _logger.LogError(ex, "An unhandled exception occurred");
  72.             await HandleExceptionAsync(context, ex);
  73.         }
  74.     }
  75.    
  76.     private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
  77.     {
  78.         var response = ApiErrorResponse.FromException(exception, context);
  79.         
  80.         context.Response.ContentType = "application/problem+json";
  81.         context.Response.StatusCode = response.Status;
  82.         
  83.         var json = JsonSerializer.Serialize(response);
  84.         await context.Response.WriteAsync(json);
  85.     }
  86. }
复制代码
6.2.4 API文档与测试
  1. // Swagger/OpenAPI配置
  2. builder.Services.AddSwaggerGen(c =>
  3. {
  4.     c.SwaggerDoc("v1", new OpenApiInfo
  5.     {
  6.         Title = "Product API",
  7.         Version = "v1",
  8.         Description = "API for managing products in the e-commerce system",
  9.         Contact = new OpenApiContact
  10.         {
  11.             Name = "Development Team",
  12.             Email = "dev@company.com"
  13.         }
  14.     });
  15.    
  16.     // 添加XML注释
  17.     var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
  18.     var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
  19.     if (File.Exists(xmlPath))
  20.     {
  21.         c.IncludeXmlComments(xmlPath);
  22.     }
  23.    
  24.     // 添加JWT认证
  25.     c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
  26.     {
  27.         Description = "JWT Authorization header using the Bearer scheme",
  28.         Type = SecuritySchemeType.Http,
  29.         Scheme = "bearer"
  30.     });
  31.    
  32.     c.AddSecurityRequirement(new OpenApiSecurityRequirement
  33.     {
  34.         {
  35.             new OpenApiSecurityScheme
  36.             {
  37.                 Reference = new OpenApiReference
  38.                 {
  39.                     Type = ReferenceType.SecurityScheme,
  40.                     Id = "Bearer"
  41.                 }
  42.             },
  43.             Array.Empty<string>()
  44.         }
  45.     });
  46. });
  47. // 集成测试示例
  48. public class ProductsApiTests : IClassFixture<WebApplicationFactory<Program>>
  49. {
  50.     private readonly WebApplicationFactory<Program> _factory;
  51.    
  52.     public ProductsApiTests(WebApplicationFactory<Program> factory)
  53.     {
  54.         _factory = factory;
  55.     }
  56.    
  57.     [Fact]
  58.     public async Task CreateProduct_ReturnsCreatedStatus()
  59.     {
  60.         // Arrange
  61.         var client = _factory.CreateClient();
  62.         var command = new CreateProductCommand
  63.         {
  64.             Name = "Test Product",
  65.             Description = "Test Description",
  66.             Price = 99.99m,
  67.             Currency = "USD",
  68.             CategoryId = Guid.NewGuid()
  69.         };
  70.         
  71.         // Act
  72.         var response = await client.PostAsJsonAsync("/api/v1/products", command);
  73.         
  74.         // Assert
  75.         response.EnsureSuccessStatusCode();
  76.         Assert.Equal(HttpStatusCode.Created, response.StatusCode);
  77.         
  78.         var createdProduct = await response.Content.ReadFromJsonAsync<ProductDto>();
  79.         Assert.NotNull(createdProduct);
  80.         Assert.Equal(command.Name, createdProduct.Name);
  81.     }
  82.    
  83.     [Fact]
  84.     public async Task GetNonExistentProduct_ReturnsNotFound()
  85.     {
  86.         // Arrange
  87.         var client = _factory.CreateClient();
  88.         var nonExistentId = Guid.NewGuid();
  89.         
  90.         // Act
  91.         var response = await client.GetAsync($"/api/v1/products/{nonExistentId}");
  92.         
  93.         // Assert
  94.         Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
  95.     }
  96. }
复制代码
6.3 小结

应用层和API设计是DDD架构中的门面,它直接面向用户和其他系统。一个好的应用层应该:

  • 职责清晰:只做协调,不做业务决策
  • 接口友好:API设计符合RESTful原则
  • 错误友好:提供清晰的错误信息和状态码
  • 文档完善:自动生成API文档,便于集成
  • 易于测试:支持单元测试和集成测试
记住,应用层是系统的门面,但不是系统的全部。保持它的简洁和专注,让领域层来处理复杂的业务逻辑。这样你的系统才能保持长期的可维护性和扩展性。
在下一章中,我们将探讨服务拆分与边界定义,这是微服务架构中的核心挑战。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册