
本文介绍在 Go 语言中基于领域驱动设计(DDD)组织多模块应用时,如何避免重复建模、复用核心实体逻辑,并合理划分模型、服务与接口职责,以支持如“租户即人员”这类跨域关联场景。
本文介绍在 go 语言中基于领域驱动设计(ddd)组织多模块应用时,如何避免重复建模、复用核心实体逻辑,并合理划分模型、服务与 interfaces 职责,以支持如“租户即人员”这类跨域关联场景。
在 Go 的 DDD 实践中,模块间共享实体逻辑(如 Tenant 本质是 Person 的一种角色)不应通过复制结构或强耦合服务调用来解决,而应依托分层抽象 + 组合 + 显式契约实现松耦合复用。
✅ 推荐架构分层方案
-
独立
models包 —— 领域模型的唯一真相源
将所有核心实体定义于此,避免跨包重复声明:// models/person.go package models type Person struct { ID uint64 `gorm:"primaryKey"` Name string Email string } // models/tenant.go package models type Tenant struct { Person // 嵌入复用字段与行为 ApartmentID uint64 `gorm:"index"` // 租赁上下文专属字段 LeaseStart time.Time }✅ 优势:
Tenant天然继承Person的全部字段与方法(如GetName()),无需手动同步;数据库迁移可统一管理。 -
领域服务接口下沉至
interfaces或contracts包
定义稳定契约,供各模块按需实现或依赖:// interfaces/people_service.go package interfaces type PeopleService interface { GetByID(id uint64) (*models.Person, error) Update(p *models.Person) error // ... 其他通用操作 }-
People模块提供具体实现(如people/service.go); -
Apartments模块仅依赖该接口,不导入People的实现包,避免循环依赖; - 若需
Tenant特有操作(如GetTenantsByApartmentID),应在tenants服务包中封装,内部组合调用PeopleService和ApartmentRepository。
-
-
新增
tenants协调包 —— 承担跨域编排职责
当业务逻辑横跨People与Apartments(如“为公寓分配租户”),应新建专用包,而非让任一模块承担对方职责:// tenants/service.go package tenants type Service struct { peopleSvc interfaces.PeopleService aptRepo interfaces.ApartmentRepository tenantRepo interfaces.TenantRepository // 可复用 GORM 操作,或直接操作 models.Tenant } func (s *Service) AssignTenantToApartment(personID, aptID uint64) error { p, err := s.peopleSvc.GetByID(personID) // 复用人员查询逻辑 if err != nil { return err } tenant := models.Tenant{ Person: *p, // 复用 Person 数据 ApartmentID: aptID, LeaseStart: time.Now(), } return s.tenantRepo.Create(&tenant) }
⚠️ 关键注意事项
-
禁止结构体复制:在
apartments包中重新定义Tenant结构体(即使字段相同)将导致数据一致性风险、序列化冲突及维护成本激增; -
接口归属清晰:
PeopleService接口应置于interfaces包,不在people或apartments包内重复声明;实现类保留在people包,消费者仅依赖接口; -
领域边界优先:
Tenant不是独立领域实体,而是Person在租赁场景下的角色投影(Role-based modeling),其生命周期与Apartment强相关,因此Tenant相关的 CRUD 应由tenants包负责,而非people包; -
数据库映射灵活处理:GORM 等 ORM 可通过嵌入结构体自动映射字段,或使用
gorm.Model+ 自定义表名(如tenants表)隔离业务视图与物理存储。
总结
DDD 在 Go 中的成功落地,关键在于模型统一、契约先行、编排分离:
-
models包保障领域对象单一权威; -
interfaces包定义模块间协作契约; -
tenants(或类似协调包)承担跨域用例编排,复用底层服务而不越界。
这种设计既避免了代码重复,又维持了各模块的自治性与可测试性,真正践行了“高内聚、低耦合”的 DDD 原则。










