skills/cruldra/skills/ddd-code-organization

ddd-code-organization

SKILL.md

使用 DDD 思想组织你的代码

核心目标

优先按业务能力、子系统、限界上下文组织代码,而不是把 models/services/repositories/schemas/api/ 这类技术类型目录平铺在项目根下。

目标不是机械套用 DDD 术语,而是让一个功能的核心代码尽量收敛在同一业务子系统内,做到高内聚、低耦合、边界清晰、易于协作和演进。

适用原则

在开始设计结构或新增模块前,先判断“这段代码属于哪个业务能力”,再决定目录位置。

始终优先回答这几个问题:

  1. 这段代码服务于哪个业务子系统?
  2. 这个子系统的核心职责是什么?
  3. 它需要暴露什么公共接口给其他子系统?
  4. 它依赖哪些共享能力,哪些依赖不应该直接发生?

如果一个需求横跨多个目录才能完成,优先怀疑目录组织方式有问题,而不是继续往现有的按类型结构里堆代码。

默认组织方式

先按业务子系统切分顶层目录,再在子系统内部按需要保留小范围的技术分层。

推荐结构:

src/
├── core/                    # 基础设施与共享内核
│   ├── config.py
│   ├── database.py
│   ├── events.py
│   └── types.py            # 跨子系统共享类型
├── customers/              # 客户子系统
│   ├── __init__.py
│   ├── models.py
│   ├── repository.py
│   ├── service.py
│   ├── schemas.py
│   ├── api.py
│   └── tasks.py
├── courses/                # 课程子系统
│   ├── __init__.py
│   ├── models.py
│   ├── repository.py
│   ├── service.py
│   ├── schemas.py
│   └── api.py
└── allocation/             # 分配引擎子系统
    ├── __init__.py
    ├── models.py
    ├── repository.py
    ├── service.py
    ├── schemas.py
    ├── api.py
    └── tasks.py

不推荐的结构:

src/
├── models/
├── services/
├── repositories/
├── schemas/
├── api/
└── tasks/

上面这种结构在项目变大后,通常会导致:

  • 一个业务功能散落在 5-7 个目录中
  • 修改一个需求需要跨多层跳转
  • 合并冲突集中在公共类型目录
  • 新成员只能看到“技术层”,看不到“业务边界”

子系统划分方法

按业务能力划分,而不是按数据库表名机械划分,也不是按前后端接口名随意划分。

优先寻找这些切分信号:

  • 明确的业务职责
  • 相对稳定的领域语言
  • 独立演进的规则集合
  • 相对清晰的数据边界
  • 可以通过接口与其他模块协作

例如:

  • customers:客户生命周期与状态流转
  • courses:课程与配置管理
  • allocation:客户分配算法与策略
  • engagement:外联、提醒、跟进
  • monitoring:仪表盘、统计、告警

不要把“用户提了一类接口”直接当成子系统,也不要把“ORM 模型很多”当成拆目录依据。

子系统内部约束

允许子系统内部存在技术分层,但这些分层只能服务该子系统,不应重新退化成全局的按类型组织。

遵循以下约束:

  1. api.py 只负责对外输入输出与路由编排。
  2. service.py 承载业务规则和用例编排。
  3. repository.py 负责数据访问。
  4. models.py 放领域模型、ORM 模型或聚合相关对象。
  5. schemas.py 放接口层 DTO / Pydantic Schema。
  6. tasks.py 只放该子系统的异步任务。
  7. 更复杂的子能力可以在子系统内部继续拆子目录,但仍然围绕该业务边界组织。

如果某个子系统开始膨胀,不要先把内容抽到根级 services/;先考虑是否需要在该子系统内部拆出更清晰的子模块。

跨子系统依赖规则

始终让依赖关系尽量单向、显式、可解释。

默认规则:

  1. 子系统之间通过公共 service 接口、应用服务或事件协作。
  2. 共享枚举、基础类型、跨域通用对象放到 core/types.py 或等价共享内核中。
  3. 基础设施能力集中在 core/,不要散落在各个业务目录重复实现。
  4. 异步解耦优先使用领域事件、消息或任务,而不是互相直接深入调用内部实现。
  5. 避免循环依赖;一旦出现循环依赖,优先检查共享类型和边界划分是否错误。

设计或重构时的工作流

1. 先识别业务地图

列出系统的主要业务能力、核心流程、外部集成点和共享基础设施。

2. 再确定子系统边界

把高度相关、一起变化的代码放入同一个子系统。不要因为“它们都是 service”就放在一起。

3. 为每个子系统定义最小骨架

只创建当前真正需要的文件。不要为了对称性无脑生成所有文件。

4. 明确共享内核

把跨子系统复用且不属于单一业务的内容收敛到 core/,尤其是共享类型、配置、数据库连接、事件总线、认证上下文等。

5. 限制跨边界访问

一个子系统调用另一个子系统时,优先通过公开接口,而不是直接 import 对方内部实现细节。

6. 输出结构说明

给出目录树时,同时说明:

  • 每个子系统负责什么
  • 为什么这样切分
  • 哪些依赖是允许的
  • 哪些共享能力放在 core/

AI 在生成代码时必须遵守

  1. 新增功能时,先判断归属子系统,再创建文件。
  2. 优先把一个功能所需的模型、服务、接口、任务放在同一业务目录下。
  3. 只有在确实跨子系统共享时,才放入 core/ 或共享模块。
  4. 解释结构设计时,使用业务语言,不要只说“分层更清晰”。
  5. 如果用户给的是按文件类型组织的旧项目,优先提出按子系统重组的方案,而不是继续复制旧结构。

明确禁止

不要默认生成这些根级目录作为主组织方式:

  • models/
  • services/
  • repositories/
  • schemas/
  • controllers/
  • tasks/

除非用户明确要求保留旧架构,或项目规模极小到没有引入子系统边界的必要,否则不要采用这种纯技术分层的顶层结构。

不要把“DDD”误解为:

  • 一定要上复杂的战术模式
  • 一定要实现完整的聚合根、领域事件、仓储抽象全家桶
  • 一定要把每个概念都拆成很多类

这里的核心是按领域和业务边界组织代码,不是为了术语而术语化。

输出模板

当用户要求设计项目结构、模块边界或重构目录时,优先输出:

  1. 业务子系统列表
  2. 每个子系统的职责
  3. 推荐目录树
  4. 跨子系统依赖规则
  5. 为什么这比按 models/services/api 平铺更合适

可直接使用这种表达:

## 推荐的业务子系统划分
- customers: 负责客户生命周期与状态流转
- courses: 负责课程与配置管理
- allocation: 负责分配规则与执行

## 推荐目录结构
```text
src/
├── core/
├── customers/
├── courses/
└── allocation/

依赖规则

  • allocation 可以依赖 customers 暴露的公共服务
  • customers 不直接依赖 allocation 内部实现
  • 共享枚举统一放在 core/types.py

判断质量的检查清单

在给出方案前,检查:

  • 一个业务功能是否仍然被拆散在多个根级技术目录里
  • 顶层目录是否主要体现业务能力而不是文件类型
  • 共享内容是否真的跨子系统复用
  • 是否存在容易形成循环依赖的边界
  • 新成员看到目录树后,能否快速理解系统有哪些业务模块

如果这些问题大多回答为“否”,说明组织方式还不够面向领域。

Weekly Installs
1
Repository
cruldra/skills
First Seen
3 days ago
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1