# 从分类维度冲突到领域驱动设计(DDD)

在分类问题中，无论是开发还是生活种，通常都习惯是单维树状结构表达的，但多数情况下，事物是多维属性的。

比如：一个关于web的博客，和一个关于web的项目，可以都归为web类，也可以分为博客类，项目类各一个。
再比如在开发中文件归属的问题，如瀑布流搜索这个类，可以归于瀑布流文件夹，也可以归于搜索。

那在工程上这种问题有什么好的思考方案吗？有的兄弟，有的。

## 问题本质

例子：

* `web博客`
* `web项目`

维度其实有两个：

1. **领域（Domain）** ：web
2. **类型（Type）** ：博客 / 项目

但目录树只能选一个主维度。

同理：

* 瀑布流搜索
  * 属于「瀑布流」能力？
  * 属于「搜索」能力？

这是典型的  **正交维度冲突** 。

---

## 工程上的解决方案

核心原则：

> **目录按“主责”划分，而不是按“属性”划分，谁承担生命周期，谁是“主责”**
>
> 目录表达主责，代码表达能力，组合表达场景。

谁是“核心”，谁做一级目录，思考：

1. 谁依赖谁？
2. 谁调用谁？
3. 谁变更更频繁？
4. 删除谁会影响谁？

---

## 1️⃣ 以“核心职责”作为一级目录

比如：

* 如果瀑布流搜索是为瀑布流页面服务

  → 主责是“瀑布流能力”
* 如果是搜索系统内部的一个召回策略

  → 主责是“搜索能力”

**谁拥有生命周期，就放在哪**。

---

## 2️⃣ 不用目录表达所有维度

不要用文件夹表达：

* 业务域
* 类型
* 技术实现
* 场景

目录只表达 **一个核心维度** 。

其他维度用：

* package命名
* interface抽象
* 领域模型
* 标签（逻辑概念）

---

## 3️⃣ 常见的工程划分法

### A. 按业务域分（推荐）

```txt
/waterfall
    search.go
/search
    recall.go
```

如果瀑布流是产品功能 → 用这种。

---

### B. 按能力分（中台型系统）

```txt

/search
    waterfall_strategy.go
    category_strategy.go

```

如果是搜索中台 → 用这种。

---

## 瀑布流搜索的最佳实践

### 电商架构里

* 瀑布流 = 页面形态
* 搜索 = 能力系统

因此常见设计：

```txt
/search
    service/
    strategy/
        waterfall.go
        category.go
```

瀑布流只是一个策略，不是一个系统。

### 一个更高级的解决方案：避免“物理分类强绑定”

大型系统会：

* 用能力模块做主结构
* 用业务编排层组合能力

例如：

```txt
/application
    waterfall_feed_app.go
/domain
    search/
    recommend/
```

瀑布流只是一个 application 层组合。

---

## 更优的设计模式：领域驱动 + 组合层

那么如果一个系统经常出现，既有多个业务域，又有多个能力维度，能不能从一开始就设计成既能表达领域，又能表达能力的结构呢？有的兄弟，有的。

DDD（领域驱动设计）提供了一个很好的思路

> 领域驱动设计（DDD） 强调以业务领域为核心，将复杂的业务逻辑封装在领域层中。组合层（也叫应用层/编排层）则负责协调多个领域服务，完成复杂的业务流程。

还是以"瀑布流搜索"为例：

用户在电商APP首页下拉加载商品瀑布流，需要：

* 根据用户偏好推荐商品
* 搜索匹配的商品
* 过滤库存、上下架状态
* 返回分页数据

### 分层设计

```txt

┌─────────────────────────────────────────────────────┐
│                    接口层 (API)                      │
│         接收请求、参数校验、返回响应                   │
└─────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────┐
│                   组合层 (Composer)                  │
│         协调多个领域服务，编排业务流程                 │
│                                                     │
│   瀑布流组合器:                                      │
│   1. 调用【用户领域】→ 获取用户偏好标签               │
│   2. 调用【搜索领域】→ 根据关键词+标签搜索商品         │
│   3. 调用【商品领域】→ 批量查询商品详情               │
│   4. 调用【库存领域】→ 过滤无货商品                   │
│   5. 调用【推荐领域】→ 对结果排序                     │
│   6. 组装最终数据返回                                │
└─────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────┐
│                   领域层 (Domain)                    │
│         每个领域独立，只关心自己的业务逻辑             │
│                                                     │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐            │
│  │ 用户领域  │ │ 搜索领域  │ │ 商品领域  │            │
│  │          │ │          │ │          │            │
│  │·用户偏好  │ │·ES搜索   │ │·商品详情  │            │
│  │·用户画像  │ │·分词处理  │ │·上下架   │            │
│  └──────────┘ └──────────┘ └──────────┘            │
│                                                     │
│  ┌──────────┐ ┌──────────┐                         │
│  │ 库存领域  │ │ 推荐领域  │                         │
│  │          │ │          │                         │
│  │·库存查询  │ │·个性化排序│                         │
│  │·库存预占  │ │·热度计算  │                         │
│  └──────────┘ └──────────┘                         │
└─────────────────────────────────────────────────────┘
                          ↓
┌─────────────────────────────────────────────────────┐
│                  基础设施层                          │
│         数据库、缓存、ES、消息队列等                  │
└─────────────────────────────────────────────────────┘
```

* 接口层：接收HTTP/RPC请求，只做参数校验和响应封装
* 组合层：编排多个领域，不含业务逻辑，只做"胶水"工作
* 领域层：核心业务逻辑。高内聚、可独立测试、可复用
* 基础设施层：数据库，缓存、外部服务调用

### 实际应用场景

通过组合层编排，同样的领域服务，通过不同的组合方式，满足不同的业务场景。这就是 领域驱动 + 组合层 的核心价值：

首页瀑布流
用户偏好 → 搜索 → 商品 → 库存 → 推荐排序

搜索结果页
关键词解析 → 搜索 → 商品 → 筛选条件

分类页瀑布流
分类 → 搜索 → 商品 → 库存

店铺商品流
店铺 → 商品列表 → 库存 → 店铺排序规则

### DDD和微服务的关系

域和微服务是两个不同的概念，领域之间通过内部函数调用，不是接口，也不是 RPC。

通常的项目历程是：早期项目、团队较小、业务简单，使用单体 + DDD，所有领域在一个服务内，通过包/模块隔离；随后中等规模、逐步拆分，模块化单体，领域独立模块，共享进程，可独立演进；最后独立扩缩需求，才需要微服务每个领域/聚合根独立部署。

以下场景可以考虑微服务化：

* 团队扩大 → 不同团队负责不同领域
* 独立扩缩容 → 搜索流量大，需单独扩容
* 技术栈差异 → 某领域需要用 Python 做 AI

### DDD目录结构示例

```txt
app/product/

├── api/                    # 接口层（Interface Adapters）
│   ├── http/
│   │   ├── spu_handler.go
│   │   ├── sku_handler.go
│   │   └── audit_handler.go
│   │
│   └── rpc/
│       └── product_rpc.go
│
├── handler/                   # 应用层/组合层（Use Case / App Service）
│   ├── waterfall_feed.go
│   ├── spu_detail.go
│   ├── spu_audit.go
│   ├── related_recommend.go
│   └── terminal_push.go
│
├── server/                        # 领域层（核心模型）
│
│   ├── product/                   # 商品限界上下文（核心域）
│   │   ├── aggregate.go           # Product 聚合根（含 SPU / SKU）
│   │   ├── entity.go
│   │   ├── value_object.go
│   │   ├── repository.go
│   │   └── domain_service.go
│   │
│   ├── audit/                     # 审核子域
│   │   ├── aggregate.go
│   │   ├── repository.go
│   │   └── domain_service.go
│   │
│   ├── inventory/                 # 库存子域
│   │   ├── aggregate.go
│   │   ├── repository.go
│   │   └── domain_service.go
│   │
│   ├── search/                    # 搜索子域（如果是本系统的一部分）
│   │   ├── repository.go
│   │   └── domain_service.go
│   │
│   └── recommendation/            # 推荐子域
│       ├── repository.go
│       └── domain_service.go
│
├── infrastructure/                # 基础设施层
│
│   ├── persistence/
│   │   ├── mysql/
│   │   ├── es/
│   │   └── redis/
│   │
│   ├── external/
│   │   ├── user_client.go
│   │   └── merchant_client.go
│   │
│   └── repository_impl/           # 所有仓储实现
│       ├── product_repo_mysql.go
│       ├── audit_repo_mysql.go
│       ├── search_repo_es.go
│       └── inventory_repo_mysql.go
│
└── bootstrap/                     # 依赖注入 / wiring
    └── wire.go
```
