
mall学习教程官网:macrozheng.com
1. 背景 DDD 向来以高门槛而文明,他内部提出了非常多且抽象晦涩难懂的概念,比如实体、值对象、领域服务、领域事件、聚合根、工厂、仓库、应用服务等,第一批涌入人员很多被这些概念击退,少数坚持下来爱好学习的人继续往深处走,迎接他们的是更多的概念,比如 CQRS、六边形架构的内六边形&外六边形、输入适配器、输出适配器、防腐层、开放主机……
少数异常坚韧者熬了无数次通宵,终于将这些概念搞明白。一边感慨设计的精妙,可以形成科学且高度结构化的解决方案。一边又在摇头,叹息落地实现的难度,自己融会贯通已经这么艰难,还怎么奢求整个团队能步调一致。
追求到手真的是一场空吗?
这个问题在很长一段时间内一直困扰着我,按高度结构化进行开发,成本太高,很多关键的类和扩展点自己都没有办法记牢。不按结构化进行开发,代码就会失控,各种逻辑耦合在一起,几千甚至上万行代码的方法慢慢涌现,项目逐渐走向失控。
直到我将 结构化、标准化、模版化 这三个概念放在一起考虑,才真正豁然开朗。
- 结构化:DDD、CQRS 给出的解决方案都是高度结构化的方案,每个组件的边界和职责清晰明了,组件间的交互关系都有明确的规则。
- 标准化:在结构化的基础上,每个组件都具备高度的标准化。尽管承载的业务流程不同,但每个组件的设计都遵循同样的规则。
- 模版化:标准化意味着会有大量重复逻辑(不是重复代码),这些重复逻辑在开发手里就是 复制-粘帖-稍作修改。
既然是重复工作那就应该由机器完成!
2. 目的 目的非常明确:
- 降低概念的记忆成本,让初级开发不在惧怕 DDD。
- 统一规范,业务流程、组件设计在团队内保存高度一致。
- 提升开发效率,逻辑重复部分交由机器完成,提升开发效率。
这或许是一个对你有用的开源项目,mall项目是一套基于 SpringBoot + Vue + uni-app 实现的电商系统(Github标星60K),采用Docker容器化部署,后端支持多模块和微服务架构。包括前台商城项目和后台管理系统,能支持完整的订单流程!涵盖商品、订单、购物车、权限、优惠券、会员、支付等功能!
- Boot项目:https://github.com/macrozheng/mall
- Cloud项目:https://github.com/macrozheng/mall-swarm
- 视频教程:https://www.macrozheng.com/video/
项目演示:

3. 功能介绍3.1. Maven 脚手架 Maven 脚手架主要实现整个项目的结构高度统一,降低新项目构建成本。
使用以下命令,可快速创建符合公司规范的项目:
mvn archetype:generate -DarchetypeGroupId=com.geekhalo.lego -DarchetypeArtifactId=services-archetype -DarchetypeVersion=0.1.39-plugin_demo-SNAPSHOT -DgroupId=com.geekhalo -DartifactId=user -Dversion=0.1.39-plugin_demo-SNAPSHOT
该命令行是基于 Maven 的构建工具,用于生成项目骨架。具体来说,这个命令执行的是 archetype 插件的 generate 目标,用于创建一个预定义项目结构的模板实例。
参数解释如下:
-DarchetypeGroupId=com.geekhalo.lego : 指定要使用的原型(archetype)所在的组ID,这是一个自定义的 Maven 组织标识符,对应于提供项目的骨架模板的组织或团体。-DarchetypeArtifactId=services-archetype : 指定要使用的原型的工件ID,这是特定于该组织下用于生成新项目的模板名称。-DarchetypeVersion=0.1.39-plugin_demo-SNAPSHOT : 设置所用原型版本,这里是一个快照版本号,表明它可能在开发过程中频繁更新。-DgroupId=com.geekhalo : 为将要生成的新项目设置组ID,这是新项目所属的Maven组织标识符。-DartifactId=user : 设置新项目的工件ID,即新项目的名字。-Dversion=0.1.39-plugin_demo-SNAPSHOT : 设置新项目的初始版本号,与原型版本保持一致,同样使用了一个快照版本。
综上所述,这条命令的作用是在本地通过Maven生成一个新的项目结构,该项目是基于 com.geekhalo.lego 组下的 services-archetype 模板,并且初始化时设置的项目信息是 groupId=com.geekhalo ,artifactId=user ,以及 version=0.1.39-plugin_demo-SNAPSHOT 。
命令执行完成后,你便可以看到新增符合公司规范的 user 模块:

这个项目是一个用户服务,它被划分为多个模块:
user-domain :包含了用户服务的核心领域模型和领域逻辑。它是六边形架构中的核心层,不直接依赖于外部系统或技术栈。user-infrastructure :包含了用户服务的具体实现细节和技术栈相关的代码。例如,数据库访问层、消息队列客户端等。user-app :实现了用户服务的应用逻辑。它依赖于其他模块(如 user-domain 、user-infrastructure )来完成业务功能。user-api :定义了用户服务对外提供的接口,也就是服务契约,包括 基于Feign的RPC契约 和 基于RocketMQ的消息契约。user-feign-service :如果用户服务需要提供给其他微服务调用,那么这里会定义 Feign 服务接口。user-feign-client :是用户服务对外提供的 SDK,它提供了对用户服务的简单易用的 API 接口,使得其他服务能够快速地集成和调用用户服务的功能。user-bootstrap :启动用户服务的引导程序。它通常包含 Spring Boot 的启动类和一些配置文件。
在根目录下,有两个重要的文件:
pom.xml :Maven 项目的配置文件,用于管理所有子模块的依赖关系和构建过程。README.md :项目的说明文档,通常包括项目简介、如何运行、如何开发等内容。
结构没有对错,不同的公司存在不同规范,这块不是关注的重点。
3.2. 自定义 Idea 插件 有了项目骨架后,接下来的重点就是业务开发。通过自定义 idea 插件,可以将规范融合到插件内,保障规范落地的同时,大幅降低开发成本。
3.2.1. 创建聚合根和视图模型 聚合根是DDD中最为重要的一个概念,也是承接业务逻辑的最小单元。
日常开发基本都是围绕聚合根进行的,对此 idea 很多功能都是围绕聚合根进行构建。
让我们使用插件新建一个聚合根 “BasicUser” 用于存储用户的基本信息。在 domain 模块下的 basic 包上点击右键,选择 lego 菜单下的 “创建 聚合根” 功能,具体如下:

在弹出的对话框中填入 聚合根类名为“BasicUser”,其他保存默认,详见:

点击左上角的 View,切换到视图模型配置:

点击 “OK” 按钮,观察项目变化:Domain 模块:

Infrastructure 模块:

App 模块:

创建一个简单的 BasicUser 聚合根,插件为我们生成一系列文件。这些文件之前都需手工完成,浪费了大量时间。
从设计上,项目采用 CQRS 进行设计,需要同时应对简单和复杂两个场景。换个说法就是:
- 业务通常从一个简单场景开始,需要满足快速开发的要求。
- 随着迭代的增加复杂性也随之增加,此时可以在不影响架构设计的前提下快速升级架构。
对于简单场景,推荐使用共享存储的 CQRS 架构,具体如下:

如果写操作和读操作两者差距巨大,推荐使用标准的 CQRS 架构,具体如下:

简单介绍完背景后,看框架帮我们生成了什么:
- 基于 DDD 的写流程
- BasicUser:这是用户服务的核心领域对象,它包含了用户的属性信息以及与之相关的业务逻辑。作为领域聚合根,它可以确保数据的一致性和完整性,并且控制了对用户状态的修改操作。
- AbstractBasicUserEvent:一个抽象事件,表示用户服务中发生的一些重要事件。这些事件可能会触发用户服务的状态变化或者引发其他行为。具体的事件类型可以通过继承该抽象类来实现。
- BasicUserCommandRepository:这是用户服务的命令存储库,负责处理对用户对象的创建、更新和删除等操作。它通常会将这些操作持久化到数据库或其他存储介质中。
- JpaBasedBasicUserCommandRepository:是用户服务的命令侧存储库的一个具体实现,它使用 JPA(Java Persistence API)来操作数据库。这种实现方式可以让开发者更专注于业务逻辑,而无需关心底层的数据访问细节。
- BasicUserCommandApplication:这是用户服务的命令侧应用服务,它封装了用户服务的业务逻辑,并协调各个组件(如领域对象、存储库等)的工作。它接收来自客户端的请求,执行相应的业务逻辑,并返回结果。
- 基于 View 的读流程
- BasicUserView:是用户服务的查询侧视图模型,它包含了用户的基本信息,但不会包含任何业务逻辑。它的主要作用是在查询时提供快速响应的服务,以满足高并发读取的需求。
- BasicUserQueryRepository:这是用户服务的查询侧存储库,负责处理对用户视图模型的查询操作。它可以从数据库、缓存或者其他快速的数据源中获取数据。
- JpaBasedBasicUserQueryRepository:这是用户服务的查询侧存储库的一个具体实现,它使用 JPA(Java Persistence API)来操作数据库。这种实现方式可以让开发者更专注于业务逻辑,而无需关心底层的数据访问细节。
- BasicUserQueryApplication:这是用户服务的查询应用服务,它封装了用户服务的查询逻辑,并协调各个组件(如视图模型、存储库等)的工作。它接收来自客户端的查询请求,执行相应的查询逻辑,并返回结果。
idea 插件帮我们完成了大部分工作,快速生成共享存储的 CQRS 业务代码骨架。
3.2.2. 创建聚合根方法 有了聚合根后,我们需要在聚合根上添加业务方法。在 BasicUser 类上右键选择 lego 下的 创建聚合根方法,如图所示:

在弹窗中填入方法名为:create,如图所示:

点击“OK”按钮,会有如下变化:



自动生成信息如下:
- 新创建的 create 目录,包含流程中的核心组件
- CreateBasicUserCommand:这是一个创建用户的命令对象,它包含了创建用户所需的所有参数和信息。当客户端想要创建一个新的用户时,它会发送这个命令对象到用户服务。
- CreateBasicUserContext:这是一个上下文对象,它包含了创建用户所需的环境信息和其他辅助信息,可以帮助操作流程更好地维护和共享数据。
- BasicUserCreatedEvent:这是一个事件对象,表示用户已经被成功创建。当用户服务接收到 CreateBasicUserCommand 命令并成功创建了一个新的用户后,它会发布这个事件对象。
- BasicUser 新增 create 业务方法
- 静态的 create 方法,用于使用 Context 对象创建 BasicUser
- 实例的 init 方法,用于对 BasicUser 进行核心状态设置、创建并发布领域事件
- BasicUserCommandApplication 新增 create 方法
- 输入 CreateBasicUserCommand 对象,协调内部组件,完成创建流程
无需编写应用服务的代码,框架会自动生成 Proxy 类来完成全部流程。开发人员只需将精力放在 create 目录下的各种组件即可,组件的协作集成全部由 lego 框架完成。
此时,你就已经具备了一个不含任何业务逻辑的 创建用户 流程。
3.2.3. 创建查询方法 在 BasicUserQueryApplication 右键选择创建 “创建查询方法”,弹出如下对话框:

我们选择分页查询,确定后,自动生成:
@QueryServiceDefinition( repositoryClass = BasicUserQueryRepository.class, domainClass = BasicUserView.class ) @Validated public interface BasicUserQueryApplication { // 分页查询方法 Page<BasicUserView> pageOf(@Valid PageByStatus query); }
打开 PageByStatus 类,完善信息如下:
@NoArgsConstructor @Data public class PageByStatus{ // 使用 status 字段进行过滤 @FieldEqualTo("status") private UserStatus status; // 分页信息 private Pageable pageable; // 排序信息 private Sort sort; }
此时什么都不需要做,你便拥有了一个检索接口。
其中 PageByStatus 为 QueryObject,通过 注解 和 特定类型的字段声明查询能力:
- @FieldEqualTo("status") 标明该字段将用于与 status 进行过滤
- Pageable 类型的字段为分页信息,可以指定页码和每页大小,完成快速分页
- Sort 类型的字段提供排序信息,用于对数据进行排序
3.2.4. 其他支持 除了生成骨架代码外,框架对核心组件也提供了支持。
3.2.4.1. 通用枚举 选择创建枚举,弹出如下对话框:

点击确定,自动生成枚举和Jpa 转化器,如果使用 MyBatis 也可以选择生成 TypeHandler。
UserStatus 代码如下:
public enum UserStatus implements CommonEnum { ; // code 为枚举的唯一标识 private final int code; // 枚举描述信息 private final String descr;
UserStatus(int code, String descr){ this.code = code; this.descr = descr; } @Override public int getCode() { return this.code; }
@Override public String getDescription() { return this.descr; } }
UserStatusConverter 代码如下:
// 基于枚举的 Code 完成持久化处理 @Converter(autoApply = true) public class UserStatusConverter extends CommonEnumAttributeConverter<UserStatus> { public UserStatusConverter(){ super(UserStatus.values()); } }
3.2.4.2. 上下文工厂 选择创建上下文工厂,弹出以下弹窗:

点击“OK” 自动生成 CreateBasicUserContextFactory 如下:
@Component @Slf4j public class CreateBasicUserContextFactory extends AbstractSmartContextFactory<CreateBasicUserCommand, CreateBasicUserContext> {
public CreateBasicUserContextFactory(){ super(CreateBasicUserCommand.class, CreateBasicUserContext.class); }
@Override public CreateBasicUserContext create(CreateBasicUserCommand cmd) { CreateBasicUserContext context = CreateBasicUserContext.apply(cmd); return context; } }
3.2.4.3. 聚合根工厂 选择创建聚合根工厂,弹出以下弹窗:

点击“确定”自动生成 BasicUserFactory 如下:
@Component @Slf4j public class BasicUserFactory extends AbstractSmartAggFactory<CreateBasicUserContext, BasicUser> {
public BasicUserFactory(){ super(CreateBasicUserContext.class, BasicUser.class); }
@Override public BasicUser create(CreateBasicUserContext context) { return BasicUser.create(context); } }
3.2.4.4. 业务验证器 右键选择“创建业务验证器”,输入类名为:CreateBasicUserPhoneValidator,自动生成代码如下:
@Component @Slf4j public class CreateBasicUserPhoneValidator extends AbstractBusinessValidator<CreateBasicUserContext> { public CreateBasicUserPhoneValidator(){ super(CreateBasicUserContext.class); } @Override public void validate(CreateBasicUserContext context, ValidateErrorHandler validateErrorHandler) { log.info("validate(context) {}", context); } }
3.2.4.5. 结果转换器 右键选择“创建结果转换器”,弹出如下对话框:

自动生成代码如下:
@Component @Slf4j public class BasicUserKeyConverter extends AbstractSmartResultConverter<BasicUser, CreateBasicUserContext, BasicUserKey> {
public BasicUserKeyConverter(){ super(BasicUser.class, CreateBasicUserContext.class, BasicUserKey.class); }
@Override public BasicUserKey convert(BasicUser agg, CreateBasicUserContext context) { // 添加转换代码 return null; } }
Github上标星60K 的电商实战项目mall,全套 视频教程(2023最新版) 已更新完毕!全套教程约40小时,共113期 ,通过这套教程你可以拥有一个涵盖主流Java技术栈的完整项目经验 ,同时提高自己独立开发一个项目的能力 ,下面是项目的整体架构图,感兴趣的小伙伴可以点击链接 mall视频教程 加入学习。

整套 视频教程 的内容还是非常完善的,涵盖了mall项目最佳学习路线、整体框架搭建、业务与技术实现全方位解析、线上Docker环境部署、微服务项目学习等内容,你也可以点击链接 mall视频教程 了解更多内容。
推荐阅读- 69K Star!这是我见过最强的开源电商系统 !!
- Github标星60K!一套完整的项目实战教程来了,主流Java技术一网打尽!
- 看了我项目中购物车、订单、支付一整套设计,同事也开始悄悄模仿了...
- 订单系统就该这么设计,稳的一批!
- 支付系统就该这么设计,稳的一批!
- 权限系统就该这么设计,稳的一批!
上一篇:投行Jefferies迎来强援!前华兴资本高管开启新征程 下一篇:小红书引流方式,每天询单1000加 |