DDD + Clean Arch 探索与演示

此篇是我对于领域驱动和整洁架构的思考和实践,有很多不足之处,请大家多评论指教。
调用关系:infra层 <-> adapter层 -> useCase层 -> domain层

  • infra层的web调用adapter层的controller
  • controller实现useCase层调用infra层的db,log,cache…

总结:有点违反单向依赖,之后可以在adapter层增加网关或门面来对接infra层,使之被同层的controller调用,实现进一步的单向依赖。这里可能需要初始化的时候进行依赖注入。如果有更好的想法,可以留言探讨。
github库

请求处理流程

请求

GET /user/1

infra 基础设施层(包含 web,UI,Device,DB,…)

// web:接受请求,并处理数据
// gin example
r.GET('/user/:id', GetUserByID)

func GetUserByID(c *gin.Context) {
 id := c.Param("id") // 提取id
 controller := adapter.UserController{C: c}
 controller.GetUserByID(id)
 }
// db:实现 domain repo 接口
// mysql example
type MysqlUserRepo struct {}
func (repo MysqlUserRepo) GetByID(id string) (*domain.User, error) {
 var user domain.User 
 if result := global.DB.Where("id = ?", id).First(&user); result.Error != nil { 
 return nil, result.Error 
 } 
 return &user, nil
 }

adapter 调度层

// controller(控制器, 调用 UseCase)
type UserController struct {
     C *gin.Context
 }
func (controller *UserController) GetUserByID(id string) {
 presenter := userPresenter{c: controller.C} 
 uc := usecase.UserUseCase{Repo: db.MysqlUserRepo{}, Output: &presenter} 
 uc.GetUserByID(id)
 }
// presenter(演示者, 实现 Use Case Output Port)
type userPresenter struct {
 c *gin.Context}
func (presenter *userPresenter) Standard(user *domain.User, err error) {
 // todo: 处理 err 
 presenter.c.JSON(http.StatusOK, gin.H{"id": user.ID})
 }

useCase 用例层

// 用户用例(实现 Use Case input Port,调用 Use Case Output Port)
// 之所以用useCase命名而不用interactor,是因为Gland提示拼写错误,看着烦
type UserUseCase struct {
 Repo   domain.UserRepo Output UserOutput}
// Use Case Output Port
type UserOutput interface {
 Standard(u *domainUser, err error)
 }
func (u *UserUseCase) GetUserByID(id string) {
 user, err := u.Repo.GetByID(id) u.Output.Standard(user, err)
 }

domain 领域层

// 实体或者聚合根
type User struct {
 ID string
 }
 // 存储库
type UserRepo interface {
 GetByID(id string) (*User, error)
 }
本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!