DDD领域驱动设计,项目结构目录说明
采用 DDD(领域驱动设计)思想搭建多租户 RBAC 系统,需要明确区分 接口层(Interface)、应用层(Application)、领域层(Domain) 和 基础设施层(Infrastructure)。下面给出一个符合 Go 语言惯例的项目结构方案,核心模型包括租户、公司、部门、用户、角色、菜单(权限),并体现多租户隔离。
一、顶层目录结构
project/
├── cmd/
│ └── server/
│ └── main.go # 程序入口,依赖组装
├── internal/
│ ├── interface/ # 接口层(用户界面)
│ │ ├── web/ # HTTP 相关(gin)
│ │ │ ├── handler/ # 请求处理器
│ │ │ ├── middleware/ # 认证、租户、权限中间件
│ │ │ ├── dto/ # 数据传输对象(请求/响应)
│ │ │ └── router/ # 路由注册
│ │ └── rpc/ # 其他协议(gRPC等),暂略
│ ├── application/ # 应用层(用例编排)
│ │ ├── user/ # 用户应用服务
│ │ ├── role/ # 角色应用服务
│ │ ├── menu/ # 菜单应用服务
│ │ ├── tenant/ # 租户应用服务
│ │ └── common/ # 通用应用服务(如认证、权限校验)
│ ├── domain/ # 领域层(核心业务逻辑)
│ │ ├── tenant/ # 租户聚合
│ │ ├── company/ # 公司聚合(或实体)
│ │ ├── department/ # 部门聚合(或实体)
│ │ ├── user/ # 用户聚合
│ │ ├── role/ # 角色聚合
│ │ ├── menu/ # 菜单聚合(权限)
│ │ ├── common/ # 共享领域(值对象、领域事件、仓储接口)
│ │ └── repository/ # 仓储接口定义(由基础设施实现)
│ └── infrastructure/ # 基础设施层
│ ├── persistence/ # 持久化(Gorm 实现)
│ │ ├── mysql/ # MySQL 具体实现
│ │ │ ├── tenant_repo.go
│ │ │ ├── user_repo.go
│ │ │ └── ...
│ │ ├── model/ # Gorm 数据模型(与领域对象分离)
│ │ └── mapper/ # 领域对象 <-> 数据模型 转换
│ ├── cache/ # 缓存(freecache/redis)
│ ├── idgen/ # ID 生成(雪花算法等)
│ ├── event/ # 事件总线(可选)
│ └── security/ # 安全(JWT、密码加密)
├── pkg/ # 可导出的公共库
│ ├── errors/ # 自定义错误
│ └── utils/ # 工具函数
├── configs/ # 配置文件
├── scripts/ # 构建脚本
└── go.mod
二、各层详细职责与依赖关系
1. 接口层(internal/interface/web)
- 职责:处理 HTTP 请求,参数解析与校验,调用应用层服务,返回响应。
- 不包含业务逻辑,只做协议转换。
- 依赖:应用层服务接口(通过依赖注入)。
- 典型文件:
handler/user_handler.go:接收 gin.Context,调用userApplicationService.Create(...)。dto/user_dto.go:请求/响应结构体(区别于领域对象)。middleware/auth.go、middleware/tenant.go、middleware/rbac.go。router/router.go:组装路由和中间件。
2. 应用层(internal/application)
- 职责:编排领域对象和基础设施,实现用例(Use Case)。事务边界通常在此层。
- 不包含领域规则,规则在领域层。
- 依赖:领域层的仓储接口、领域服务、其他应用服务。
- 结构:按聚合根拆分子包(如
user、role、tenant),每个子包包含:service.go:应用服务(如UserApplicationService),方法名体现用例(如CreateUser、AssignRoles)。assembler.go:(可选)将 DTO 转换为领域对象或值对象。
- 示例:
application/user/service.go中的CreateUser会调用userRepo.Save(),并可能发布领域事件。
3. 领域层(internal/domain)
- 核心:表达业务概念、规则、逻辑。
- 包含:
- 聚合根(Aggregate Root):如
User、Role、Tenant、Company、Department、Menu。 - 实体(Entity):如
UserRole(关联实体)、MenuNode。 - 值对象(Value Object):如
Email、Permission、TenantID、UserID。 - 领域事件(Domain Event):如
UserCreatedEvent、RolePermissionChangedEvent。 - 仓储接口(Repository Interface):定义在
domain/repository/中,如UserRepository、RoleRepository。 - 领域服务(Domain Service):处理跨聚合的业务逻辑,如
AuthorizationService(检查权限)、TenantProvisioningService(创建租户时初始化角色/菜单)。
- 聚合根(Aggregate Root):如
- 多租户隔离:领域对象中应包含
TenantID值对象,仓储接口方法强制接收tenantID。 - 权限模型:
Menu聚合包含Permission值对象;Role聚合包含RoleMenus实体集合。
4. 基础设施层(internal/infrastructure)
- 职责:实现领域层定义的接口,提供技术支撑。
- 子目录:
persistence/mysql/:实现UserRepository、RoleRepository等,使用 Gorm 操作数据库。persistence/model/:Gorm 的持久化模型(与领域对象分离,如UserPO、RolePO)。persistence/mapper/:领域对象与 PO 之间的转换。cache/:实现缓存接口(如RolePermissionCache)。idgen/:实现 ID 生成器。event/:实现事件发布(同步/异步)。
- 注意:基础设施层应通过依赖注入提供给应用层,不直接依赖上层。
三、聚合划分与边界
基于业务模型,可以划分以下聚合(推荐以聚合根为边界):
| 聚合根 | 包含实体/值对象 | 仓储 |
|---|---|---|
| Tenant | 租户基本信息、状态、套餐配置 | TenantRepo |
| Company | 公司名称、法人、地址,下级公司(自关联) | CompanyRepo |
| Department | 部门名称、负责人,上级部门(自关联) | DeptRepo |
| User | 用户名、密码、归属部门,关联的角色列表(角色ID集合) | UserRepo |
| Role | 角色名称、类型(模板/自定义),含菜单权限集合 | RoleRepo |
| Menu | 菜单节点、权限标识、父子关系 | MenuRepo |
其中 Company 和 Department 在简单场景下可以不是聚合根,而是作为 Tenant 下的实体,但为了职责清晰,仍可单独拆出。注意:User 与 Role 的关系通常通过 UserRole 关联实体处理,但为简化,可在 User 聚合中持有 []RoleID,或在 Role 聚合中持有 []UserID。建议在 User 聚合中维护角色列表,因为用户是操作主体。
四、关键层间交互示例(仅描述)
- HTTP 请求 →
handler解析参数生成 DTO。 - Handler 调用
application/user/service.CreateUser(dto)。 - Application Service:
- 校验 DTO 基本合法性。
- 从仓储获取必要的数据(如检查用户名是否重复,部门是否存在)。
- 创建领域对象
User(通过工厂方法)。 - 调用
userRepo.Save(user)(基础设施实现)。 - 提交事务(数据库事务边界在应用层)。
- 发布
UserCreatedEvent。
- 领域层的
User对象包含业务规则(如用户名不能包含特殊字符、密码必须加密)。 - 基础设施层的
UserRepositoryImpl将User对象转换为UserPO,使用 Gorm 存入数据库。
五、处理跨聚合操作举例(租户初始化)
当创建新租户时,需要:
- 创建租户聚合(租户根)。
- 创建默认公司/部门。
- 克隆平台模板角色和菜单到该租户下(写操作)。
这些逻辑涉及多个聚合(Tenant、Company、Department、Role、Menu),属于领域服务,可放在 domain/tenant/tenant_provisioning_service.go,由应用层调用。
六、依赖注入与组装
在 cmd/server/main.go 中:
- 初始化 Gorm DB、Redis、FreeCache 等。
- 创建各个仓储实现(基础设施层)。
- 创建领域服务、应用服务(依赖仓储接口)。
- 创建 Handler(依赖应用服务)。
- 将 Handler 注册到 Gin Router。
七、总结
上述结构遵循 DDD 分层,核心业务逻辑封装在 domain,外部依赖(Web、DB、Cache)在 infrastructure 和 interface。多租户隔离体现在:TenantID 作为值对象贯穿领域层;仓储接口方法均接收 tenantID;应用层和中间件确保当前租户上下文正确传递。
该方案适合中大型项目,保持高内聚低耦合,便于长期演进和测试。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)