news 2026/9/19 22:55:13

一个用例四个入口:domain-driven-hexagon同命令支持HTTP、CLI、消息与GraphQL

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一个用例四个入口:domain-driven-hexagon同命令支持HTTP、CLI、消息与GraphQL

一个用例四个入口:domain-driven-hexagon同命令支持HTTP、CLI、消息与GraphQL

【免费下载链接】domain-driven-hexagonLearn Domain-Driven Design, software architecture, design patterns, best practices. Code examples included项目地址: https://gitcode.com/gh_mirrors/do/domain-driven-hexagon

在 domain-driven-hexagon(领域驱动六边形架构)开源项目中,"创建一个用户"这同一个业务用例,竟然同时支持 HTTP 接口、命令行、消息队列和 GraphQL 四种调用方式,而且业务逻辑只写一遍。这就是六边形架构 + CQRS 的魅力:命令(Command)是唯一入口,四种调用方式都只是"接口适配器"。本文带你快速看懂这一设计,适合刚接触 DDD 和后端架构的新手。

为什么同一个命令能有多个入口?

传统写法是"每个接口一套逻辑":HTTP 控制器里写一遍、消息监听里再写一遍,很快就会出现重复代码和不一致行为。

domain-driven-hexagon 的思路是:把业务动作抽象为命令对象,所有入口只负责"把参数变成命令",剩下的全部交给命令处理器。架构图如下,左侧的 HTTP、CLI、消息队列等入口都通过 DTO 汇入核心层:

核心关键词就藏在图中:Interface Adapters(接口适配器)负责各种入口,Application Core(应用核心)只包含命令处理器与领域实体

四个入口,四种"壳"

"创建用户"用例位于 src/modules/user/commands/create-user/ 目录下,一眼就能看全四种入口:

入口文件适用场景
HTTPcreate-user.http.controller.ts浏览器/移动端 API 调用
CLIcreate-user.cli.controller.ts运维脚本、定时任务
消息队列create-user.message.controller.ts微服务间异步解耦
GraphQLcreate-user.graphql-resolver.ts前端按需取数的 BFF 层

每个入口的代码结构惊人地一致,可以概括为三步走:

  1. 解析参数:HTTP 从@Body()取,CLI 从位置参数取,消息从user.create事件取,GraphQL 从@Args('input')取;
  2. 构造命令:都执行new CreateUserCommand(参数)
  3. 投递执行:都调用commandBus.execute(command)

以消息入口为例,create-user.message.controller.ts 通过@MessagePattern('user.create')订阅微服务消息,收到消息后仅仅把它转成CreateUserCommand交给 CommandBus,没有任何业务逻辑。CLI 入口 create-user.cli.controller.ts 则注册了new user <email> <country> <postalCode> <street>这样的控制台命令,方便在终端直接创建用户。

💡 入口之间的差异(参数校验、错误码、响应格式)都留在了"壳"里,互不干扰。比如 HTTP 入口在捕获UserAlreadyExistsError时返回 409 Conflict,见 create-user.http.controller.ts 中的match分支。

命令与处理器:逻辑只有一份

命令对象定义在 create-user.command.ts 中,只包含 email、country、postalCode、street 四个纯数据字段,不含任何处理逻辑

真正干活的是命令处理器 create-user.service.ts:

  • 用命令参数构建UserEntity聚合根与Address值对象;
  • 事务内插入用户,保证领域事件原子性地被消费;
  • 成功返回Ok(用户ID),重复则返回Err(UserAlreadyExistsError)

这种Result类型(而非抛异常)的返回值,让四个入口都能用同一套方式判断成功或失败。参数校验则由请求 DTO 统一承担,如 create-user.request.dto.ts 用class-validator约束邮箱格式与长度。

四种入口、命令、处理器、模块注册的关联关系,在 user.module.ts 中一目了然:httpControllersmessageControllerscliControllersgraphqlResolvers分组注册,共享同一批commandHandlers

隐藏彩蛋:领域事件联动钱包 🎁

通过任意一个入口创建用户后,系统都会发布UserCreatedDomainEvent领域事件。钱包模块的事件处理器 create-wallet-when-user-is-created.domain-event-handler.ts 监听到事件后自动为用户创建钱包。

这意味着:无论用户是走 HTTP、CLI、消息还是 GraphQL 进入系统,"自动开户"的行为都 100% 一致——因为触发点不在入口,而在领域事件。这正是六边形架构"依赖倒置"的直观收益。

如何跑起来体验一下

git clone https://gitcode.com/gh_mirrors/do/domain-driven-hexagon cd domain-driven-hexagon npm install npm run start:dev

启动后访问http://localhost:3000/docs查看 Swagger 文档,尝试POST /api/v1/user;也可以使用项目内置 Docker 环境 docker/docker-compose.yml 一键拉起依赖。

小结:这套模式能迁移到你自己的项目吗?

✅ 适用:同一业务动作有多个触发来源(API、定时任务、第三方回调、异步消息)时;

✅ 适用:希望把"入口协议"与"业务规则"彻底解耦,方便分别测试与替换时;

⚠️ 注意:小项目可能用不上全部四层,作者也在 README 中提醒——以上建议是推荐而非教条,可按需裁剪。

一句话总结:让命令成为唯一事实来源,让 HTTP、CLI、消息、GraphQL 都只是可插拔的适配器——这就是 domain-driven-hexagon 教给我们的、最实用的六边形架构实践。

【免费下载链接】domain-driven-hexagonLearn Domain-Driven Design, software architecture, design patterns, best practices. Code examples included项目地址: https://gitcode.com/gh_mirrors/do/domain-driven-hexagon

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 22:52:51

无障碍可点击区域与菲茨定律:移动端 48x48px 触控防御实战

无障碍可点击区域与菲茨定律&#xff1a;移动端 48x48px 触控防御实战在移动端人机交互与触控界面设计中&#xff0c;最容易引发用户强烈挫败感与误触愤怒的&#xff0c;莫过于**“极度细小脆弱的可点击热区&#xff08;Tiny Touch Targets&#xff09;”**&#xff1a; 用户在…

作者头像 李华
网站建设 2026/9/19 22:46:20

基于深度强化学习的智能停车系统设计与优化

1. 项目背景与核心价值停车难问题已经成为现代城市管理的痛点。我在北京某商业区实地调研时发现&#xff0c;高峰时段平均每辆车需要绕行15分钟才能找到空位&#xff0c;这不仅造成时间浪费&#xff0c;更导致周边道路拥堵指数上升37%。传统停车引导系统往往只显示剩余车位数量…

作者头像 李华