通过GO后端项目实践理解DDD架构
最近在工作过程中重构的项目要求使用DDD架构,在网上查询资料发现教程五花八门,并且大部分内容都是长篇的概念讲解,晦涩难懂,笔者看了一些github上入门的使用DDD的GO项目,并结合自己开发中的经验,谈谈自己对DDD的理解。
相关项目
https://github.com/takashabe/go-ddd-sample
基本的CURD,代码简洁,适合快速上手 DDD
https://github.com/eyazici90/go-ddd
适合理解第一个项目后,有一定基础
https://github.com/ThreeDotsLabs/wild-workouts-go-ddd-example
比较复杂,适合进阶
简要介绍
这里借用github老哥的一张图来说明,那些长篇大论就不叙述了,因为咱们主要是在代码中应用嘛,直接讲解在项目中要怎么写。
DDD主要分为几层,interface,application,domain,infrastructure
在DDD中的调用顺序是 interface -> application -> domain
可以类比MVC controller -> service -> model
可以发现DDD比MVC多了一个infrastructure层,这里用某张表的CURD来举例,这里的某张表在domain层中被称为某个仓储(Repository),其实就是这张表的CURD操作,只不过你在domain定义的是接口,接口中方法的技术实现在infrastructure定义。
也就是说你的业务规则在domain定义,业务逻辑中与其他系统(db,文件系统,网络,第三方库等)的交互写在infrastructure层。
举个简单的例子,对于用户表(用户仓储),在domain层定义接口
internal/domain/user/user.go
// UserRepository 表示用户仓库接口
// 由基础设施层实现
type UserRepository interface {Get(ctx context.Context, id int) (*domain.User, error) // 根据ID获取用户GetAll(ctx context.Context) ([]*domain.User, error) // 获取所有用户Save(ctx context.Context, user *domain.User) error // 保存用户
}
要实现接口中定义的方法,就需要涉及到数据库(这里是Mysql)的CURD操作, Mysql不属于本系统,所以方法的实现放在infrastructure
internal/infrastructure/user/user.go
// userRepository 实现 UserRepository 接口
type userRepository struct {conn *sql.DB // 数据库连接
}// NewUserRepository 返回初始化的 UserRepository 实现
func NewUserRepository(conn *sql.DB) repository.UserRepository {return &userRepository{conn: conn}
}// Get returns domain.User
// Get 根据ID返回用户对象
func (r *userRepository) Get(ctx context.Context, id int) (*domain.User, error) {row, err := r.queryRow(ctx, "select id, name from users where id=?", id)if err != nil {return nil, err}u := &domain.User{}err = row.Scan(&u.ID, &u.Name)if err != nil {return nil, err}return u, nil
}// GetAll returns list of domain.User
// GetAll 返回所有用户列表
func (r *userRepository) GetAll(ctx context.Context) ([]*domain.User, error) {rows, err := r.query(ctx, "select id, name from users")if err != nil {return nil, err}defer rows.Close()us := make([]*domain.User, 0)for rows.Next() {u := &domain.User{}err = rows.Scan(&u.ID, &u.Name)if err != nil {return nil, err}us = append(us, u)}return us, nil
}// Save saves domain.User to storage
// Save 将用户对象保存到存储
func (r *userRepository) Save(ctx context.Context, u *domain.User) error {stmt, err := r.conn.Prepare("insert into users (name) values (?)")if err != nil {return err}defer stmt.Close()_, err = stmt.ExecContext(ctx, u.Name)return err
}
完成domain接口的定义以及实现后,相当于MVC的model层实现了,那么只需要在上层调用domain的接口就行了
DDD中的interface,application和MVC的controller和service层一样的,interface用于接收http请求,把参数传递到下层,application用于整合业务逻辑,把http接口需要的数据返回
internal/application/user/user.go 整合数据返回给interface层
// UserInteractor 嵌入了domain的用户CURD接口
type UserInteractor struct {Repository repository.UserRepository
}// GetUser returns user
// GetUser 返回指定ID的用户
func (i UserInteractor) GetUser(ctx context.Context, id int) (*domain.User, error) {return i.Repository.Get(ctx, id)
}
internal/interface/user/user.go 拿到数据返回给前端
// Handler 用户处理器
type Handler struct {UserInteractor *application.UserInteractor
}// getUser 处理获取单个用户的请求
func (h Handler) getUser(w http.ResponseWriter, r *http.Request, id int) {ctx := r.Context()user, err := h.UserInteractor.GetUser(ctx, id)if err != nil {Error(w, http.StatusNotFound, err, "failed to get user") // 获取用户失败return}JSON(w, http.StatusOK, user)
}
一个简单的DDD架构就这样实现了
有几点经验
- domain层的一个领域负责的是相关的业务,也就是对 /domain/xxx 这个目录,只要与这个领域相关的表都可以定义在该目录下
- domain除了定义接口,相关的业务逻辑实现也要放在这里,比如对领域中的某些参数的校验,即在该领域定义的结构体绑定一些校验方法等,但是与第三方交互的具体技术实现都放在infrastructure
- 对于想采用CQRS的项目,一般是在application层分别定义查询实例和命令实例,把涉及到查询的操作都绑定到查询实例,把涉及到命令的操作都绑定到命令示例,可以读写分离,写操作可以写主库,读操作读从库
相关文章:
通过GO后端项目实践理解DDD架构
最近在工作过程中重构的项目要求使用DDD架构,在网上查询资料发现教程五花八门,并且大部分内容都是长篇的概念讲解,晦涩难懂,笔者看了一些github上入门的使用DDD的GO项目,并结合自己开发中的经验,谈谈自己对…...
精益数据分析(2/126):解锁数据驱动的商业成功密码
精益数据分析(2/126):解锁数据驱动的商业成功密码 大家好!在如今这个数据爆炸的时代,数据就像一座蕴含无限宝藏的矿山,等待着我们去挖掘和利用。最近我在深入研读《精益数据分析》这本书,收获了…...
天线静电防护:NRESDTLC5V0D8B
一. 物联网天线的使用环境 1.1 联网天线广泛应用于智能家居领域,比如智能门锁、智能摄像头等设备中,通过天线实现设备与家庭网络的连接,用户可以远程控制和监控家居设备。以智能摄像头为例,它通过天线将拍摄的画面实时传输到用户…...
【Linux 并发与竞争】
【Linux 并发与竞争】 Linux是一个多任务操作系统,肯定会存在多个任务共同操作同一段内存或者设备的情况,多个任务甚至中断都能访问的资源叫做共享资源,就和共享单车一样。在驱动开发中要注意对共享资源的保护,也就是要处理对共享…...
实用类题目
1. 密码强度检测 题目描述:生活中,为保证账户安全,密码需要有一定强度。编写一个方法,接收一个字符串作为密码,判断其是否符合以下强度要求:长度至少为 8 位,包含至少一个大写字母、一个小写字…...
STM32F103C8T6-基于FreeRTOS系统实现步进电机控制
引言 上一篇文章讲述了如何使用蓝牙连接stm32进行数据收发控制步进电机,这篇在之前的基础上通过移植操作系统(FreeRTOS或者其他的也可以,原理操作都类似)实现步进电机控制。 上篇博客指路:STM32蓝牙连接Android实现云…...
macOS安装java
一、下载 官网Java Downloads | Oracle 安装载java8,下载对应的JDK Java Downloads | Oracle 二、双击安装 安装 完成 三、查看安装位置 打开终端窗口,执行命令: /usr/libexec/java_home -V /Library/Java/JavaVirtualMachines/jdk-1.8.jdk/Content…...
ArrayList的subList的数据仍是集合
ArrayList的subList结果不可强转成ArrayList,否则会抛出 ClassCastException异常 • 级别: 【CRITICAL】 • 规约类型:BUG • 最坏影响: 程序错误,抛出异常 说明:subList 返回的是ArrayList的内部类SubL…...
zkmall模块商城:B2C 场景下 Vue3 前端性能优化的广度探索与实践
ZKmall作为面向B2C场景的模块化电商平台,其前端性能优化在Vue3框架下的实践融合了架构设计、渲染机制与业务特性,形成了一套多维度的优化体系。以下从技术实现与业务适配两个维度展开分析: 一、Vue3响应式系统深度适配 Proxy驱动的精准更新…...
【Netty篇】Future Promise 详解
目录 一、 Netty Future 与 Promise —— 异步世界的“信使”与“传话筒”🚀1、 理解 Netty Future2、 理解 Netty Promise 二、 代码案例解读💻例1:同步处理任务成功👍例2:异步处理任务成功📲例3ÿ…...
第3.3节:print函数详解
1 第3.3节:print函数详解 print是AWK中最常用的输出函数,它可以将指定的变量、字段或表达式的值输出到标准输出。以下是其详细用法: 1.1 基本语法 print (expr1), (expr2), ..., (exprN)。其中expr1, expr2, ..., exprN是要输出的表达式&a…...
视频分析设备平台EasyCVR安防视频管理系统,打造电石生产智能视频监控新体系
一、背景介绍 电石生产中的出炉工序是整个生产流程中最为繁重且危险的环节。在开堵炉眼的过程中,电石极易发生飞溅现象,尤其在进行吹氧操作时,人员灼伤的风险极高。鉴于此,该工序正逐步由传统的人工操作模式向智能化方向转变。然…...
C语言中的.操作符与->操作符的区别
.操作符->操作符访问方式成员访问操作符(类,结构体的成员)指针来访问对象的成员语法形式对象.成员指针->成员举例 person是结构体的对象,里面有一个age是成员变量 person.age 例如,如果有一个指向 person 结构体…...
从 PyTorch 到 ONNX:深度学习模型导出全解析
在模型训练完毕后,我们通常希望将其部署到推理平台中,比如 TensorRT、ONNX Runtime 或移动端框架。而 ONNX(Open Neural Network Exchange)正是 PyTorch 与这些平台之间的桥梁。 本文将以一个图像去噪模型 SimpleDenoiser 为例&a…...
Android 应用添加Tile到SystemUI QuickSettings
安卓源码里有谷歌给的关于 Tile 的说明。 frameworks/base/packages/SystemUI/docs/qs-tiles.md SystemUI QuickSettings 简称QS,指的是 下拉菜单里的区域。区域里的一个选项就是一个 Tile 。 下图是 frameworks/base/packages/SystemUI/docs/ 里的附图示例&#…...
【MySQL】前缀索引、索引下推、访问方法,自适应哈希索引
最左前缀原则 对于INDEX(name, age)来说最左前缀可以是联合索引的最左N个字段, 也可以是字符串索引的最左M个字符。 SELECT * FROM t WHERE name LIKE 张%其效果和单独创建一个INDEX(name)的效果是一样的若通过调整索引字段的顺序, 可以少维护一个索引树, 那么这个顺序就是需要…...
C++中变量、函数存储、包括虚函数多态实现机制说明
从C语言转到C开发,对于类内变量的存储,类内函数的存储,存在疑惑; 子类如何继承父类的变量的?如果子类和父类变量同名了怎么办?C中,函数有了作用域,类内函数,只能是这个类或对应对…...
Android Studio开发知识:从基础到进阶
引言 Android开发作为移动应用开发的主流方向之一,曾吸引了无数开发者投身其中。然而,随着市场饱和和技术迭代,当前的Android开发就业形势并不乐观,竞争日益激烈。尽管如此,掌握扎实的开发技能仍然是脱颖而出的关键。本…...
数据加载与保存
通用方式 SparkSQL提供了通用的数据加载方式,使用spark.read.loa方法,并可通过format指定数据类型(如csv、jdbc、json、orc、parquet、textFile)。 load方法后需传入数据路径(针对csv、jdbc、json、orc、parquet、…...
linux命令九
系统服务 常见重点配置 Listen:监听地址:端口(80) ServerName:本站点注册的DNS名称(空缺) DocumentRoot:网页根目录(/var/www/html) DirectoryIndex:起始…...
ocr-身份证正反面识别
在阿里云官网,申请一个token [阿里官方]身份证OCR文字识别_API专区_云市场-阿里云 (aliyun.com) 观察一下post请求body部分json字符串,我们根据这个创建一个java对象 先默认是人像面 public class IdentityBody {public String image;class configure…...
PCIE Link Equalizaton
1. 均衡概述 link均衡是用于通过调整tx rx的参数来提高信号质量, 所有有LTSSM关联的lane都需要进行均衡操作。均衡只有在低速率的均衡完成之后才可以进行后续高速度的均衡,虽然协议规定可以重复做均衡,但是还是不建议进行同意速率的重复多次均…...
单节锂电池4.2V升压5V都有哪些国产芯片推荐?国产SL4011高效,高性价比
针对单节锂电池(4.2V)升压至5V应用中 SL4011升压芯片 的核心优势解析,结合其技术参数与典型应用场景进行详细说明: 1. 宽输入电压与高兼容性 输入范围:2.7V-12V,完美覆盖单节锂电池全周期电压(3…...
机器学习 | 神经网络介绍 | 概念向
文章目录 📚从生物神经元到人工神经元📚神经网络初识🐇激活函数——让神经元“动起来”🐇权重与偏置——调整信息的重要性🐇训练神经网络——学习的过程🐇过拟合与正则化——避免“死记硬背” 👀…...
视频孪生重构施工逻辑:智慧工地的数字化升级
当"智慧工地"概念在2017年首次写入《建筑业发展"十三五"规划》时,行业普遍将其等同于摄像头与传感器的简单叠加。十年数字浪潮冲刷下,智慧工地的内涵已发生本质跃迁:从工具层面的信息化改造,进化为基于视频数…...
六根觉性:穿透表象的清净觉知之光
在喧嚣的禅堂里,老禅师轻叩茶盏,清脆的声响划破沉寂。这声"叮"不仅震动耳膜,更叩击着修行者的心性——这正是佛教揭示的六根觉性在世间万相中的妙用。当我们凝视《楞严经》中二十五圆通法门,六根觉性犹如六道澄明之光&a…...
spring:注解@Component、@Controller、@Service、@Reponsitory
背景 spring框架的一个核心功能是IOC,就是将Bean初始化加载到容器中,Bean是如何加载到容器的,可以使用spring注解方式或者spring XML配置方式。 spring注解方式直接对项目中的类进行注解,减少了配置文件内容,更加便于…...
【树形dp题解】dfs的巧妙应用
【树形dp题解】dfs的巧妙应用 [P2986 USACO10MAR] Great Cow Gathering G - 洛谷 题目大意: Bessie 正在计划一年一度的奶牛大集会,来自全国各地的奶牛将来参加这一次集会。当然,她会选择最方便的地点来举办这次集会。 每个奶牛居住在 N N …...
Halcon应用:九点标定-手眼标定
提示:若没有查找的算子,可以评论区留言,会尽快更新 Halcon应用:九点标定-手眼标定 前言一、Halcon应用?二、应用实战1、图形理解[eye-to-hand]:1.1、开始应用2 图形理解[eye-in-hand] 前言 本篇博文主要用…...
【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(一)
自动引用计数 前言alloc/retain/release/dealloc实现苹果的实现 autoreleaseautorelease实现苹果的实现 总结 前言 此前,写过一遍对自动引用计数的简单学习,因此掠过其中相同的部分:引用计数初步学习 alloc/retain/release/dealloc实现 由于…...
