DDD 也就是所谓的领域驱动设计, 强调设计对应的领域模型, 然后将对应的领域模型和我们的代码相关联, 这样可以构建一个防腐层, 来隔离我们的变化。
1. 基础模块
按照我习惯我喜欢把项目分成四个模块,下面是对应的
- web: 表示对应的路由
- service: 表示我们项目提供的服务, 比如对应的登录服务
- domian: 用来表示我们的领域模型
- repo: 用于数据库的相关操作
- dao: 通常用来表示对应的数据库相关的实体和操作
上面这四个东西可能名字不同, 但是内核其实都是一致的。
但是上面这些概念的范围比较大, 实操起来还是有点困难, 下面我就使用Go 语言的一些组件, 比如说对应的gin 框架的一些概念。
2. 扩展模块
首先在 gin 框架中是存在一个中间件的概念,也就是所谓的midware, 我们可以基于这个中间件完成一些限流, JWT 认证相关东西, 这个中间件该放在哪一个位置呢, 我认为应该是放在 web 下面
其次在一些场景下我们还有一些基础的组件, 比如使用 redis 实现对应的限流操作, 那么像这种模块该放在什么下面呢, 这种模块其实我们也可以单独开一个模块这个模块和上面的web 模块这样平起平坐, 叫做 lib 模块。
3. 代码实现
好了说了那么多, 我们直接看代码, 看一看每一个模块的职责:
首先是 web 模块, 我们设置对应的路由:
//1. 定义我们的路由
func (user *UserHandler) Route(server *gin.Engine) {
group := server.Group("/user")
group.POST("/signup", user.SignUp)
group.POST("/login", user.LoginJWT)
group.GET("/profile", user.ProfileJwt)
}
//2. 定义我们的请求格式:
type LoginReq struct {
Email string `json:"email"`
Password string `json:"password"`
}
其次是 domain 模块, domain 中的模型是和我们的service 密切相关的:
type User struct {
Id int64
Email string
Password string
ConfirmPassWord string
Ctime time.Time
}
比如我们service 中的一个函数, 接收的是由 web 层面传递过来的 数据, 返回的是 domain 中的模型和当前 service 出现的错误, 注意这个错误是我们希望可以在 web 层面感受到的
func (svc *UserService) Profile(ctx context.Context, id int64) (domain.User, error){
user, err := svc.repo.FindById(ctx, id)
if err != nil {
return domain.User{}, err
}
return user, nil
}
最后就是我们 repo 层面完成数据库相关的操作, 注意这里的数据库通常不特指某一种, 可以是redis, 可以是我们的mysql,所以我们可以在这一层面对这些数据结构进行封装, 比如我在下面封装了两种东西, 第一种是对应的userdao, 第二种是对应的缓存
type UserRepo struct {
dao *dao.UserDAO
cache *cache.UserCache
}