用 Spring 思维理解 DDD —— 以 Kratos 为参照

​ 在此前的学习工作中,使用的开发框架一直都是 SpringBoot,对 MVC 架构几乎是肌肉记忆:Controller 接请求,Service 写业务逻辑,Mapper 操作数据库,这套套路早已深入骨髓。
​ 最近开始学习 Go,为了写微服务,接触到了 Kratos 框架,也顺带深入了解了 DDD(领域驱动设计)。一开始,我的第一反应是——“这不就是 MVC 换个说法吗?”
但越学越发现,虽然 DDD 和我们熟悉的 Spring MVC 分层在形状上很像,但它对“业务逻辑该放哪、数据访问该放哪”划分得更严格,还用工程化的方式强制执行这些规则。
​ 这篇文章,我就用自己熟悉的 Java Spring 思维,把 DDD 的分层思想翻译成 Spring 语言,再对照 Kratos 在 Go 里的实现,帮你快速搞懂它们的异同。

在这里插入图片描述

往期博客

Go语言新手村:轻松理解变量、常量和枚举用法
Go 语言中的结构体、切片与映射:构建高效数据模型的基石

1. Spring 的常见分层

在 Java 项目中,最典型的分层是:

Controller → Service → Mapper → DB
  • Controller:接收请求、参数校验、调用 Service
  • Service:执行业务逻辑(有时混合数据访问)
  • Mapper:访问数据库(MyBatis Mapper / JPA Repository)

这种分层没错,而且配合团队自律,也能写出很干净的项目结构。但现实是:

  • Service 往往既写业务规则,又写 SQL 条件拼接;
  • 业务规则分散在 Service、Mapper 甚至 Controller 中;
  • 一旦底层数据访问方式变化(换数据库、换 RPC),改动会影响大量上层代码。

2. DDD 的目标:边界清晰、职责单一

DDD 要解决的,就是让业务逻辑与技术实现彻底解耦,做到:

  • 业务规则集中在领域模型中,贴着数据维护不变式;
  • 数据访问细节被隔离在仓储实现中,随时可替换;
  • 跨聚合的编排逻辑集中在应用服务(Usecase)中,清晰可测。

DDD 的典型分层

接口层(Controller / API)→ 应用层(Usecase / Application Service)→ 领域层(Entity / Aggregate / Domain Service / Repository 接口)→ 基础设施层(Repository 实现 / 外部服务实现)

3. 用 Spring 语言对照 DDD 分层

DDD 层次Kratos 对应Spring 对应职责
接口层(API)serviceController参数校验、鉴权、DTO ↔ Domain 转换
应用层(Usecase)bizService(理想状态)编排业务流程、事务控制、调用多个领域对象或外部服务
领域层repo实体类、领域服务接口维护业务不变式、暴露行为方法、定义仓储接口
基础设施层dataMapper / Feign 实现层数据持久化、调用远程服务、模型映射(PO ↔ Domain)

4. 核心理念对比

4.1 Repository

  • Spring 常见写法:Mapper/Repository 接口直接返回 PO(数据库模型),业务层可能直接用它判断。
  • DDD 写法:仓储接口定义在领域层,返回的是领域对象(封装了业务行为的方法),由基础设施层实现。

4.2 业务规则的位置

  • 常见误区:在 Service 里写 if (user.getEnabled() == 0) throw ...
  • DDD 方式:在领域对象中提供 ensureActive(),领域对象自己决定什么是可用

4.3 应用服务(Usecase)

  • 职责:一次完整业务流程的编排、事务边界、调用多个仓储接口、发布领域事件。
  • 不做的事:不直接写 SQL,不去判断 user.getEnabled(),不实现底层细节。

5. 案例:锁单流程

下面的锁单方法只是为了演示 DDD 分层思路,并不具备生产可用性。在真实系统中,锁单流程往往要面对并发控制一致性等复杂问题。

5.1 常见 Spring 写法(简化版)

@Service
@AllArgsConstructor
public class OrderService {private final OrderMapper orderMapper;private final UserMapper userMapper;private final StockMapper stockMapper;private final WalletMapper walletMapper;@Transactionalpublic String lockOrder(String userId, String sku, int count) {// 校验账户是否可用if (!userMapper.ensureActive(userId)) {throw new BizException("用户不可用");}// 校验库存if (!stockMapper.hasStock(sku, count)){throw new BizException("商品库存不足");}// 检查余额if (!walletMapper.hasBalance(userId, count * price)){ throw new BizException("余额不足");}// 预减库存stockMapper.reserveStock(...);// 冻结余额walletMapper.freezeBalance(...);// 添加一条订单记录orderMapper.insertOrder(...);return orderId;}
}

缺点:业务判断和数据访问混杂

5.2 DDD 写法(Spring 风格)

领域层(实体类 + 仓储接口)
// 聚合根:用户
public class User {public void ensureActive() { /* 校验用户有效性 */ }
}// 聚合根:库存
public class Stock {public void reserve(int count) { /* 校验库存并预留 */ }
}// 聚合根:钱包
public class Wallet {public void freeze(double amount) { /* 校验余额并冻结 */ }
}// 仓储接口
public interface UserRepo { User load(String id); void save(User u); }
public interface StockRepo { Stock load(String sku); void save(Stock s); }
public interface WalletRepo { Wallet load(String uid); void save(Wallet w); }
public interface OrderRepo { void save(Order o); }
应用层(用例编排)
@Service
@AllArgsConstructor
public class LockOrderUsecase {private final UserRepo userRepo;private final StockRepo stockRepo;private final WalletRepo walletRepo;private final OrderRepo orderRepo;public void lock(String userId, String sku, int qty, double price) {// 调用仓储接口,获取领域对象User user = userRepo.load(userId);Stock stock = stockRepo.load(sku);Wallet wallet = walletRepo.load(userId);// 业务编排user.ensureActive();stock.reserve(qty);wallet.freeze(qty * price);// 持久化数据orderRepo.save(new Order());stockRepo.save(stock);walletRepo.save(wallet);}
}
  • 业务逻辑在领域对象:例如ensureActivereservefreeze方法,他们只关心业务实现,不关心数据是怎么获取的
  • 数据访问集中在仓储实现:Repo 不做业务判断,取出来交给实体自己判断
  • 应用层清晰编排流程:用 Repo 加载实体 → 调用实体方法做判断/修改 → 再通过 Repo 保存变更

6. Kratos 如何落地

Kratos 在 Go 里用目录结构 + wire 静态注入强制执行这种依赖方向:

service(接口层) → biz(用例+仓储接口) → data(仓储实现) → DB/远程服务
  • biz 中不能 import ORM/HTTP 客户端等具体库;
  • data 中实现所有仓储接口,负责 PO ↔ Domain 映射;
  • service 只负责接收请求、调用 Usecase。

这跟 Spring 在理想状态下的分层几乎一致,但 Kratos 用工程手段物理防止越层,减少团队自律成本。

7. 总结

用 Spring 开发者的眼光看:

  • DDD 并不是要你放弃 Controller/Service/Mapper,而是让 Service 变成 应用服务,专注业务编排;
  • 业务判断应该写在领域对象中,不应该在 Mapper 或 Service 里直接写;
  • Repository 接口定义在领域层,实现放在基础设施层;

领域模型对外暴露的是业务语义,数据访问实现细节被封装在仓储里,上层业务不感知底层变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/92869.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92869.shtml
英文地址,请注明出处:http://en.pswp.cn/web/92869.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docspace|Linux|使用docker完全离线化部署onlyoffice之docspace文档协作系统(全网首发)

一、 前言 书接上回,Linux|实用工具|onlyoffice workspace使用docker快速部署(离线和定制化部署)-CSDN博客,如果是小公司或者比如某个项目组内部使用,那么,使用docspace这个文档协同系统是非常合适的&…

【教程】如何高效提取胡萝卜块根形态和颜色特征?

胡萝卜是全球不可或缺的健康食材和重要的经济作物, 从田间到餐桌,从鲜食到深加工,胡萝卜在现代人的饮食和健康中扮演着极其重要的角色,通过量化块根形态和色泽均匀性,可实现对高产优质胡萝卜品种的快速筛选。工具/材料…

Python初学者笔记第二十四期 -- (面向对象编程)

第33节课 面向对象编程 1. 面向对象编程基础 1.1 什么是面向对象编程面向过程:执行者 耗时 费力 结果也不一定完美 面向对象:指挥者 省时 省力 结果比较完美面向对象编程(Object-Oriented Programming, OOP)是一种编程范式,它使用"对象&…

Go 语言 里 `var`、`make`、`new`、`:=` 的区别

把 Go 语言 里 var、make、new、: 的区别彻底梳理一下。1️⃣ var 作用:声明变量(可以带初始值,也可以不带)。语法: var a int // 声明整型变量,默认值为 0 var b string // 默认值 ""…

计算机网络---IP(互联网协议)

一、IP协议概述 互联网协议(Internet Protocol,IP)是TCP/IP协议族的核心成员,位于OSI模型的网络层(第三层),负责将数据包从源主机传输到目标主机。它是一种无连接、不可靠的协议,提供…

DataFun联合开源AllData社区和开源Gravitino社区将在8月9日相聚数据治理峰会论坛

🔥🔥 AllData大数据产品是可定义数据中台,以数据平台为底座,以数据中台为桥梁,以机器学习平台为中层框架,以大模型应用为上游产品,提供全链路数字化解决方案。 ✨杭州奥零数据科技官网&#xff…

【工具】通用文档转换器 推荐 Markdown 转为 Word 或者 Pdf格式 可以批量或者通过代码调用

【工具】通用文档转换器 推荐 可以批量或者通过代码调用 通用文档转换器 https://github.com/jgm/pandoc/ Pandoc - index 下载地址 https://github.com/jgm/pandoc/releases 使用方法: 比如 Markdown 转为 Word 或者 Pdf格式 pandoc -s MANUAL.txt -o example29.docx …

【UEFI系列】Super IO

文章目录一、什么是Super IO二、Super IO的作用常见厂商三、逻辑设备控制如何访问SIO逻辑设备的配置寄存器具体配置数值四、硬件监控(hardware monitor)一、什么是Super IO Super Input/Output超级输入输出控制器。 通过LPC(low pin count&a…

飞算 JavaAI 2.0.0 测评:自然语言编程如何颠覆传统开发?

一、前言 在AI技术高速发展的今天,编程方式正在经历一场革命。传统的“手写代码”模式逐渐被AI辅助开发取代,而飞算JavaAI 2.0.0的推出,更是让自然语言编程成为现实。 作为一名长期使用Java开发的程序员,我决定深度体验飞算Java…

Dubbo + zk 微服务

一、安装zk注册中心 win版本:windows环境下安装zookeeper教程详解(单机版)-CSDN博客 linux版本: 二、服务提供方搭建 引入dubbo和zk依赖 提供接口 使用注解方式实现接口级注册到zk,而springcloud是将服务注册到注册…

聆思duomotai_ap sdk适配dooiRobot

一、说明 1、duomotai_ap介绍 duomotai_ap是一个针对多模态开发板(如 CSK6-MIX 开发板)的大模型 AI 开发套件 SDK,主要用于开发语音、视觉等多模态 AI 应用。 2、dooiRobot介绍 基于Doly 机器人的经典外观设计,采用聆思CSK6011A…

Photoshop软件打开WebP文件格的操作教程

Photoshop软件打开WebP文件格的操作教程,好吧,这是英文原版: Photoshop 23.2 原生支持 WebP 格式,无需插件即可打开、编辑和保存 WebP 文件。用户可通过“文件 > 另存为副本”选择 WebP 格式,调整无损/有损压缩及质…

【数据结构】——顺序表链表(超详细解析!!!)

目录一. 前言二. 顺序表1. 顺序表的特点2. 代码实现三. 链表1. 单向链表代码实现2.双向链表代码实现四. 顺序表与链表的区别总结一. 前言 顺序表和链表是最基础的两种线性表实现方式。它们各有特点,适用于不同的应用场景。本文将详细介绍这两种数据结构的实现原理、…

GitHub的简单使用方法----(4)

在安装完git之后,桌面右键会出现两个git的选项第一个gui打开是这样的用户界面分别是新建仓库,克隆仓库,打开已经存在的仓库。tips:Git Gui 默认只能操作本地仓库——它本质上是一个图形化的“本地 Git 客户端”。 它本身不内置“下载远程仓库…

蓝桥杯----大模板

在写大模板之前,先讲一个函System_Init(),用于系统初始化关闭所有LED与外设,关闭所有LED就是传入0xff数据打开锁存器,关闭外设就是传入0x00打开锁存器。现在所有底层已经提供给大家了,先提供最简单版本的大模板&#x…

科技写作改革我见:取消参考文献,以点读率取代引证率!

科技写作改革我见:综述应取消参考文献,学术成就评估以点读下载率取代参考文献引证率!李升伟 张君飞 韩若兰引言在当今信息爆炸的时代,科技写作作为知识传播的核心载体,其形式与评价体系正面临前所未有的挑战。传统…

【Altium designer】快速建立原理图工程的步骤

快速建立原理图工程的步骤产品规格书分析 整理产品需求,明确主控芯片、外围接口类型、总线频率、电源需求及隔离要求、PCB尺寸等关键信息。使用文本清单列出所有需求,确保无遗漏。硬件需求架构图绘制 根据需求说明书和收集的信息,使用VISIO绘…

Origin2025b安装包免费,附Origin 2025安装教程

老规矩先放链接:origin2025b安装包 有位小粉丝问我有没有Origin2025b的安装包,有的兄弟有的,只有你想不到,没有小兔找不到的软件。 这个origin是OriginLab公司开发的一个科学绘图、数据分析的软件,Origin支持各种各样…

【C++语法】输出的设置 iomanip 与 std::ios 中的流操纵符

文章目录【C语法】输出的设置 iomanip 与 std::ios 中的流操纵符1. iomanip 中的流操纵方法1.1 位宽操作类1.1.1 std::setw(x)1.1.2 std::setfill(c)1.1.3 std::left1.1.4 std::right1.1.5 std::internal1.2 小数操作类1.2.1 std::fixed1.2.2 std::setprecision(x)1.2.3 std::s…

go语言学习笔记-Map

map 是一种无序的基于 key-value 的数据结构,Go 语言中的 map 是引用类型,必须初始化 才能使用。 Go 语言中 map 的定义语法如下map[KeyType]ValueType常见两种创建方法1 使用map初始化var scoreMap make(map[string]int, 8) scoreMap["陈翔"…