当前位置: 首页 > news >正文

DDD-017:六边形架构(Hexagonal Architecture)

DDD-017:六边形架构(Hexagonal Architecture)

17.1 六边形架构概述

17.1.1 什么是六边形架构?

【原理】

六边形架构(Hexagonal Architecture),又称端口与适配器架构(Ports and Adapters Architecture),由 Alistair Cockburn 于 2005 年提出。其核心思想是将应用程序分为内部和外部,内部是核心业务逻辑,外部是技术实现细节,两者通过端口和适配器进行交互

六边形架构的核心原则:

  • 内外分离:核心业务逻辑独立于技术实现
  • 依赖倒置:外部依赖内部,而非内部依赖外部
  • 端口定义:内部定义交互接口(端口)
  • 适配器实现:外部实现具体技术细节(适配器)

17.1.2 架构示意图

┌──────────────────────────────────────────────────┐ │ 外部世界 │ │ Web UI / REST API / CLI / MQ / External Services │ └───────────────┬──────────────────┬───────────────┘ │ │ ┌───────────────┼──────────────────┼───────────────┐ │ ▼ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ Rest Adapter │ │ MQ Adapter │ │ │ │ (入站适配器) │ │ (出站适配器) │ │ │ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ ┌──────────┴─────────────┴────────────────────┴──────────────┴──────────┐ │ 六边形边界(应用边界) │ │ ┌─────────────────────────────────────────────────────────────────┐ │ │ │ 应用程序核心 │ │ │ │ │ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ │ │ 入站端口 │ │ 出站端口 │ │ │ │ │ │ (Driving Port) │ │ (Driven Port) │ │ │ │ │ │ OrderUseCase │ │ OrderRepository │ │ │ │ │ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ │ │ │ │ │ │ ┌──────────────────┐ │ │ │ │ │ └─────▶│ 应用服务 │◀─┘ │ │ │ │ │ OrderApplication │ │ │ │ │ │ Service │ │ │ │ │ └────────┬─────────┘ │ │ │ │ │ │ │ │ │ ┌────────▼─────────┐ │ │ │ │ │ 领域模型 │ │ │ │ │ │ Order Aggregate │ │ │ │ │ └──────────────────┘ │ │ │ │ │ │ │ └─────────────────────────────────────────────────────────────────┘ │ │ │ └───────────────────────────────────────────────────────────────────────────┘ │ │ ┌───────────────┼────────────────────┼───────────────┐ │ ▼ ▼ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ JPA Adapter │ │ Redis Adapter │ │ │ │ (出站适配器) │ │ (出站适配器) │ │ │ └────────┬────────┘ └────────┬────────┘ │ │ │ │ │ └─────────────┬┴────────────────────┴┬─────────────┘ │ │ ┌─────────────▼──────────────────────▼─────────────┐ │ 基础设施 │ │ MySQL Database / Redis / Kafka │ └──────────────────────────────────────────────────┘

17.1.3 核心概念

概念说明示例
端口(Port)定义应用程序与外界的交互接口OrderRepository、OrderUseCase
适配器(Adapter)实现端口,连接具体技术JPA Repository、REST Controller
入站端口(Driving Port)定义外部如何调用应用OrderUseCase 接口
出站端口(Driven Port)定义应用如何调用外部OrderRepository 接口
入站适配器(Driving Adapter)实现入站端口调用REST Controller
出站适配器(Driven Adapter)实现出站端口接口JPA Repository

17.2 端口与适配器详解

17.2.1 入站端口(Driving Port)

【原理】

入站端口定义了外部世界如何驱动应用程序执行业务用例。它是应用程序暴露给外部的服务接口。

入站端口特点:

  • 定义在应用层或领域层
  • 以用例为中心命名(如 CreateOrder、CancelOrder)
  • 表达业务意图,不暴露技术细节
  • 通常由 Application Service 实现

【代码示例】

// ========== 入站端口定义 ==========packagecom.example.order.application.port.in;/** * 订单用例接口(入站端口) * 定义外部可以执行的业务操作 */publicinterfaceOrderUseCase{/** * 创建订单 */OrderIdcreateOrder(CreateOrderCommandcommand);/** * 支付订单 */voidpayOrder(PayOrderCommandcommand);/** * 取消订单 */voidcancelOrder(CancelOrderCommandcommand);/** * 查询订单详情 */OrderDetailQueryResultqueryOrderDetail(OrderIdorderId);}// ========== 命令对象 ==========@Data@BuilderpublicclassCreateOrderCommand{privateUserIduserId;privateList<OrderItemData>items;privateCouponCodecouponCode;privateShippingAddressshippingAddress;@Data@BuilderpublicstaticclassOrderItemData{privateProductIdproductId;privateQuantityquantity;}}@Data@BuilderpublicclassPayOrderCommand{privateOrderIdorderId;privatePaymentMethodpaymentMethod;privateMoneyamount;}@Data@BuilderpublicclassCancelOrderCommand{privateOrderIdorderId;privateStringreason;}// ========== 查询结果 ==========@Data@BuilderpublicclassOrderDetailQueryResult{privateOrderIdorderId;privateOrderStatusstatus;privateMoneytotalAmount;privateLocalDateTimecreateTime;privateList<OrderItemResult>items;@Data@BuilderpublicstaticclassOrderItemResult{privateProductIdproductId;privateStringproductName;privateMoneyprice;privateQuantityquantity;privateMoneysubtotal;}}

17.2.2 出站端口(Driven Port)

【原理】

出站端口定义了应用程序需要被驱动的外部依赖。它是应用程序对基础设施的需求声明。

出站端口特点:

  • 定义在领域层或应用层
  • 以能力命名(如 Repository、EventPublisher)
  • 只定义接口,不关心实现
  • 由基础设施层的适配器实现

【代码示例】

// ========== 出站端口定义 ==========packagecom.example.order.application.port.out;/** * 订单仓储接口(出站端口) * 定义数据持久化能力 */publicinterfaceOrderRepository{/** * 保存订单 */Ordersave(Orderorder);/** * 根据ID查询订单 */Optional<Order>findById(OrderIdorderId);/** * 根据用户ID查询订单列表 */List<Order>findByUserId(UserIduserId);/** * 删除订单 */voiddelete(Orderorder);}/** * 事件发布接口(出站端口) * 定义事件发布能力 */publicinterfaceEventPublisher{/** * 发布领域事件 */voidpublish(List<DomainEvent>events);/** * 发布单个事件 */defaultvoidpublish(DomainEventevent){publish(List.of(event));}}/** * 库存服务接口(出站端口) * 定义库存扣减能力 */publicinterfaceInventoryService{/** * 预留库存 */booleanreserveStock(ProductIdproductId,Quantityquantity);/** * 释放库存 */voidreleaseStock(ProductIdproductId,Quantityquantity);/** * 确认扣减库存 */voidconfirmDeduction(ProductIdproductId,Quantityquantity);}/** * 支付服务接口(出站端口) * 定义支付处理能力 */publicinterfacePaymentService{/** * 发起支付 */PaymentResultpay(PaymentRequestrequest);/** * 查询支付状态 */PaymentStatusqueryStatus(PaymentIdpaymentId);/** * 退款 */RefundResultrefund(RefundRequestrequest);}

17.2.3 入站适配器(Driving Adapter)

【原理】

入站适配器实现对外部请求的接收,并将其转换为对入站端口的调用。

入站适配器特点:

  • 处理特定技术协议(HTTP、MQ、CLI等)
  • 负责请求解析、参数验证、异常处理
  • 调用入站端口执行业务
  • 将结果转换为响应格式

【代码示例】

// ========== 入站适配器 - REST Controller ==========packagecom.example.order.adapter.in.web;@RestController@RequestMapping("/api/orders")@RequiredArgsConstructorpublicclassOrderController{privatefinalOrderUseCaseorderUseCase;// 注入入站端口@PostMappingpublicResponseEntity<OrderResponse>createOrder(@RequestBody@ValidCreateOrderRequestrequest){// 1. 将请求DTO转换为命令对象CreateOrderCommandcommand=toCommand(request);// 2. 调用入站端口OrderIdorderId=orderUseCase.createOrder(command);// 3. 返回响应returnResponseEntity.status(HttpStatus.CREATED).body(newOrderResponse(orderId.getValue()));}@PostMapping("/{orderId}/payment")publicResponseEntity<Void>payOrder(@PathVariableLongorderId,@RequestBody@ValidPayOrderRequestrequest){PayOrderCommandcommand=PayOrderCommand.builder().orderId(OrderId.of(orderId)).paymentMethod(request.getPaymentMethod()).amount(Money.of(request.getAmount())).build();orderUseCase.payOrder(command);returnResponseEntity.ok().build();}@PostMapping("/{orderId}/cancellation")publicResponseEntity<Void>cancelOrder(@PathVariableLongorderId,@RequestBody@ValidCancelOrderRequestrequest){CancelOrderCommandcommand=CancelOrderCommand.builder().orderId(OrderId.of(orderId)).reason(request.getReason()).build();orderUseCase.cancelOrder(command);returnResponseEntity.ok().build();}@GetMapping("/{orderId}")publicResponseEntity<OrderDetailResponse>getOrder(@PathVariableLongorderId){OrderDetailQueryResultresult=orderUseCase.queryOrderDetail(OrderId.of(orderId));returnResponseEntity.ok(toResponse(result));}// ========== 转换方法 ==========privateCreateOrderCommandtoCommand(CreateOrderRequestrequest){returnCreateOrderCommand.builder().userId(UserId.of(request.getUserId())).items(request.getItems().stream().map(item->CreateOrderCommand.OrderItemData.builder().productId(ProductId.of(item.getProductId())).quantity(Quantity.of(item.getQuantity())).build()).collect(Collectors.toList())).couponCode(request.getCouponCode()!=null?CouponCode.of(request.getCouponCode()):null).shippingAddress(toAddress(request.getShippingAddress())).build();}}// ========== 入站适配器 - 消息消费者 ==========packagecom.example.order.adapter.in.messaging;@Component@RequiredArgsConstructorpublicclassOrderEventListener{privatefinalOrderUseCaseorderUseCase;@KafkaListener(topics="payment-completed",groupId="order-service")publicvoidonPaymentCompleted(PaymentCompletedEventevent){// 将消息事件转换为命令PayOrderCommandcommand=PayOrderCommand.builder().orderId(OrderId.of(event.getOrderId()
http://www.rkmt.cn/news/1463209.html

相关文章:

  • 2026年北京钢铁租赁行业现状与专业选型分析 - 品牌企业推荐师(官方)
  • 别再死记硬背了!用Python和NumPy从零理解张量:从标量到视频数据的直观建模
  • GPT-3.5微调实战指南:企业专属ChatGPT构建方法
  • 提升openwfd开发效率:用快马平台智能生成高性能编码与传输模块
  • 想做硬件工程师?高考志愿填报与职业全攻略:芯片·嵌入式·板级硬件深度解析
  • 告别网盘限速烦恼:这款免费工具让你下载速度飙升500%
  • 2026年PDF全能转换指南:保留过渡效果与超链接,5款工具实测对比 - 时时资讯
  • 8分钟预测千只股票:Kronos AI如何用基础模型重塑你的投资决策?
  • Qbot量化交易框架:本地化AI投研平台架构深度解析与实战部署
  • 为什么选择BigVGAN-v2_22khz_80band_256x?揭秘其在多语言语音与环境音效生成中的优势
  • 2026年北京农村自建房用什么瓦好?不锈钢瓦/铝镁锰瓦/彩石金属瓦深度测评:金宸伯领先 - 企业深度横评dyy6420
  • MiniCPM-V-4-GPTQ安全与优化:确保模型稳定运行的10个最佳实践
  • 3步掌握PDF全能工具箱,轻松处理各类文档难题
  • 安卓本地仓库管理App源码:三类用户权限区分+SQLite数据存储+全界面流程实现
  • 如何彻底修复DWPose姿态估计器报错:3种实用解决方案
  • 11万英语发音宝库:打造你的专属离线发音助手
  • 2026年 通信电缆厂家推荐榜单:市话电缆/矿用通讯电缆/室内通信电缆/自承式通信电缆/防水通信电缆/地埋通信电缆优质品牌精选 - 企业推荐官【官方】
  • NF-κB通路:炎症与癌症发展的关键枢纽
  • 终极炉石传说增强插件HsMod:55项功能打造个性化游戏体验
  • 抖音批量下载工具终极指南:从零开始掌握无水印视频下载
  • 2026年论文党必备:一键生成论文工具测评与推荐清单
  • 如何用MOOTDX在5分钟内搭建专业级量化交易系统:从数据获取到策略实现的完整指南
  • 原生技术,赋能视频孪生;镜像视界空间计算,成就顶尖视频孪生
  • 3分钟解决Cursor试用限制:设备标识重置的终极技术指南
  • Oracle数据库锁表一小时,我靠这3个SQL脚本定位到元凶(附实战排查流程)
  • 运筹优化老鸟的私房菜:Benders分解在产能规划与供应链问题中的实战调参指南
  • 空间视觉重建技术,打造园区顶尖全域视频孪生管控体系
  • 终极指南:5分钟掌握GitHut,解锁GitHub编程语言趋势可视化
  • 2026年 南通门墙柜一体化定制推荐榜:极简同色/轻奢统色/全屋收纳定制,实力厂家与精装改造口碑解析 - 品牌企业推荐师(官方)
  • Gemma 4本地部署实战:普通人零门槛运行可嵌入微信/Obsidian的轻量AI