AI 时代,为什么我们更需要 Deep Module?

过去几十年,软件工程一直在追求一件事:

把复杂的软件拆开。

我们拆分函数。

拆分类。

拆分模块。

拆分服务。

拆分微服务。

拆得越来越细之后,我们通常会认为:

模块越小,职责越单一,系统就越容易维护。

但今天,当 AI 开始真正参与软件开发时,我越来越怀疑:

“拆得更细”真的是正确的方向吗?

也许,我们真正需要的不是更多模块。

而是:

更深的模块。

这就是 Deep Module。


一、什么是 Deep Module?

Deep Module 的概念来自 John Ousterhout 在《A Philosophy of Software Design》中提出的软件设计思想。

理解 Deep Module,可以先看一个非常简单的模型:

模块 = 接口 + 实现

一个模块对外暴露的是接口。

模块内部隐藏的是实现。

那么,一个模块的“深度”可以简单理解为:

它提供的能力,与它暴露的复杂度之间的比例。

可以理解成:

模块深度 = 模块提供的能力
          ─────────────
          模块对外暴露的复杂度

一个模块能够提供大量能力,但只需要用户理解一个简单接口。

那么它就是一个 Deep Module。

相反,如果一个模块本身没有提供多少能力,却要求调用者理解大量细节,那么它就是一个 Shallow Module。


二、一个经典例子:Unix 的文件系统

文件系统是 Deep Module 的典型例子。

应用程序调用:

FileInputStream input = new FileInputStream("data.txt");

或者:

Files.readAllBytes(path);

接口非常简单。

但文件系统内部实际上隐藏了大量复杂性:

  • 磁盘块管理
  • 文件索引
  • 缓存
  • 文件描述符
  • 权限控制
  • 并发访问
  • 文件系统结构
  • IO 调度
  • 异常恢复

调用者不需要理解这些东西。

它只需要知道:

我要读取一个文件。

这就是 Deep Module。

┌──────────────────────────────┐
│           简单接口            │
│                              │
│      read(file)              │
├──────────────────────────────┤
│                              │
│                              │
│        巨大的内部能力         │
│                              │
│ 文件系统、缓存、磁盘、权限等   │
│                              │
└──────────────────────────────┘

模块内部很复杂。

但是模块外部很简单。

这就是:

Deep Module 的核心价值:把复杂性藏在模块内部。


三、我们今天的软件,为什么越来越“浅”?

现代软件开发中有一个非常有趣的现象。

我们一直强调:

  • 单一职责
  • 高内聚
  • 低耦合
  • 小函数
  • 小类
  • 小模块

这些原则本身没有问题。

但它们经常被过度理解。

于是代码开始变成这样:

OrderController
        ↓
OrderApplicationService
        ↓
OrderDomainService
        ↓
OrderValidator
        ↓
OrderAssembler
        ↓
OrderRepository
        ↓
OrderMapper

一个“创建订单”的功能,可能只包含几百行代码。

但开发者却需要在:

7 个类、多个包、多个抽象层

之间不断跳转。

每一个类都很小。

每一个类都有自己的职责。

从局部来看,它们都很“干净”。

但从整体来看:

理解一个简单业务,却需要穿越大量模块。

这就是一个非常典型的问题:

局部简单,整体复杂。


四、Shallow Module 的真正问题

假设我们有两个设计。

设计 A:大量浅模块

CreateOrderController

CreateOrderService

CreateOrderValidator

CreateOrderAssembler

OrderRepository

OrderMapper

InventoryService

PriceService

每个模块都很小。

每个模块的接口也很简单。

但要完成一个任务,你必须理解:

模块 A
   ↓
模块 B
   ↓
模块 C
   ↓
模块 D
   ↓
模块 E

问题来了:

复杂性并没有消失。

它只是从模块内部转移到了:

模块之间。

于是开发者需要不断理解:

  • 谁调用谁?
  • 数据从哪里来?
  • 为什么这里要转换 DTO?
  • 这个逻辑应该放在哪一层?
  • 为什么这个 Service 又调用了另一个 Service?
  • 修改一个功能会影响哪些类?

代码虽然被拆开了。

但理解成本却增加了。


设计 B:一个 Deep Module

OrderModule

createOrder()

cancelOrder()

payOrder()

confirmOrder()

内部:

┌──────────────────────────────┐
│         Order Module         │
│                              │
│ createOrder()                │
│                              │
├──────────────────────────────┤
│                              │
│ 参数校验                      │
│ 商品校验                      │
│ 价格计算                      │
│ 库存检查                      │
│ 订单创建                      │
│ 数据持久化                    │
│                              │
└──────────────────────────────┘

外部调用者只需要知道:

orderModule.createOrder(command);

至于:

  • 如何验证
  • 如何计算价格
  • 如何创建订单
  • 如何保存订单

这些复杂性被隐藏起来。

这就是 Deep Module。


五、Deep Module 并不意味着“大模块”

这里非常容易产生一个误解。

Deep Module ≠ Big Module。

一个模块代码很多,并不代表它就是 Deep Module。

Deep Module 的核心不是:

代码多。

而是:

能力强,但接口简单。

例如:

orderService.createOrder(command);

这个接口可能非常简单。

但它内部可能协调:

  • 商品
  • 库存
  • 优惠
  • 价格
  • 支付
  • 风控

它仍然可以是一个 Deep Module。

相反,一个只有几十行代码的模块:

OrderDtoAssembler

可能只有一个:

convert()

看起来很简单。

但它提供的价值非常有限。

如果调用者还必须理解:

  • 输入对象是什么
  • 输出对象是什么
  • 为什么需要转换
  • 转换应该发生在哪里

那么它可能就是一个典型的 Shallow Module。

所以:

Deep 和代码规模没有直接关系。

真正重要的是:

模块对外的复杂度,是否远小于它内部承担的复杂度。


六、为什么 Deep Module 在 AI 时代更加重要?

这才是我认为最值得讨论的部分。

过去,软件主要是给人开发和维护的。

但今天的软件正在出现一个新的参与者:

AI。

AI 不只是帮我们自动补全代码。

它开始参与:

  • 阅读代码
  • 理解业务
  • 修改功能
  • 修复 Bug
  • 重构代码
  • 编写测试
  • 创建新模块

这意味着:

软件架构开始需要考虑 AI 的理解成本。


七、AI 最怕的东西:上下文碎片化

AI 与人类开发者有一个非常重要的区别。

一个经验丰富的开发者长期在一个项目中工作。

他会逐渐形成:

项目的心智模型。

他知道:

  • 订单逻辑在哪里
  • 库存逻辑在哪里
  • 哪个 Service 是核心入口
  • 哪些代码只是 DTO 转换
  • 哪些类只是技术层

但 AI 每次进入一个任务,都需要重新建立上下文。

例如:

“修改创建订单逻辑。”

AI 需要找到:

Controller
   ↓
Application Service
   ↓
Domain Service
   ↓
Validator
   ↓
Assembler
   ↓
Repository
   ↓
Mapper

然后还可能需要:

Product Service
Inventory Service
Coupon Service
Payment Service

AI 必须不断:

搜索 → 阅读 → 推理 → 搜索 → 阅读 → 推理

模块越碎。

上下文越分散。

AI 的理解成本越高。


八、AI 时代真正重要的是:上下文局部性

对于 AI 来说,一个非常重要的概念是:

Context Locality(上下文局部性)。

一个功能相关的信息,最好尽可能集中。

例如:

order/
│
├── CreateOrder.java
├── CreateOrderHandler.java
├── Order.java
├── OrderRepository.java
├── OrderPolicy.java
└── OrderModule.java

AI 想修改订单。

它进入:

order/

大部分相关上下文就在附近。

而不是:

controller/order/
service/order/
repository/order/
mapper/order/
domain/order/

到处寻找。

这就是为什么我越来越认为:

AI 时代的软件架构,应该从“技术分层优先”转向“上下文完整性优先”。


九、Deep Module 的真正价值:压缩复杂性

Deep Module 对 AI 最大的价值,并不是“模块更少”。

而是:

它可以压缩 AI 需要理解的复杂性。

假设一个模块内部有 1000 行复杂代码。

但是它对外只暴露:

OrderResult createOrder(CreateOrderCommand command);

那么对于其他模块来说:

这 1000 行代码不需要理解。

它们只需要理解:

输入是什么?

输出是什么?

模块保证什么?

失败会发生什么?

这实际上是在做:

Complexity Compression(复杂性压缩)。

内部:

1000 行复杂逻辑
库存
价格
优惠
校验
事务

        ↓ Deep Module

外部:

createOrder(command)

复杂性没有消失。

但它被压缩到了一个明确的边界之后。


十、AI 不需要理解整个系统

传统软件开发经常存在一种隐性假设:

开发者最终应该理解整个系统。

但对于大型系统来说,这越来越不现实。

而 AI 时代,我们应该接受一个新的事实:

任何一次任务,都不应该要求理解整个系统。

修改订单:

理解 Order Module

修改库存:

理解 Inventory Module

修改支付:

理解 Payment Module

模块之间通过稳定接口协作。

这意味着:

系统复杂,但任务上下文可以简单。

这正是 Deep Module 最重要的价值。


十一、Deep Module 与 Domain Module

Deep Module 并不是要求把整个系统塞进一个巨大模块。

更合理的方式是:

系统先按照领域拆分。

例如:

system
│
├── order
│
├── inventory
│
├── payment
│
├── product
│
└── user

每一个领域模块:

都是一个相对独立的自治单元。

例如:

Order Module
│
├── 对外能力
│     ├── createOrder
│     ├── cancelOrder
│     └── queryOrder
│
└── 内部实现
      ├── 订单规则
      ├── 状态机
      ├── 数据访问
      └── 技术实现

这样形成一个非常重要的架构思想:

领域模块之间保持明确边界。

模块内部可以拥有复杂实现。

模块外部只需要理解能力接口。

这就是:

Domain Autonomy + Deep Module

领域自治单元 + 深模块。


十二、这与传统分层架构有什么区别?

传统架构通常是:

Controller Layer

Service Layer

Repository Layer

Infrastructure Layer

系统按照:

技术职责

进行组织。

Deep Module 更强调:

Order Module

Inventory Module

Payment Module

系统首先按照:

业务能力

进行组织。

然后模块内部再组织技术结构。

例如:

order
│
├── interfaces
│
├── application
│
├── domain
│
└── infrastructure

这两者最大的区别是:

传统横向架构

Controller
├── OrderController
├── PaymentController
└── InventoryController

Service
├── OrderService
├── PaymentService
└── InventoryService

理解订单,需要跨越整个系统。


领域自治架构

Order
├── Controller
├── Application
├── Domain
└── Infrastructure

Payment
├── Controller
├── Application
├── Domain
└── Infrastructure

理解订单,只需要进入:

Order

这对于 AI 非常重要。


十三、AI 时代的软件架构,需要新增一个指标

过去我们评价架构时,通常关注:

  • 可维护性
  • 可扩展性
  • 性能
  • 可测试性
  • 解耦程度

但 AI 时代,我认为应该增加一个新的指标:

AI Understandability

AI 可理解性。

一个架构应该让 AI 能够快速回答:

这个功能在哪里?

这个模块负责什么?

修改这个功能需要看哪些代码?

这个模块依赖谁?

谁可以调用这个模块?

修改这里可能影响什么?

如果这些问题需要跨越几十个目录、上百个文件。

那么即使架构理论上非常优雅:

它可能也不是 AI 友好的架构。


十四、未来的软件模块,应该像“能力黑盒”

我认为未来一个好的模块应该越来越像:

┌──────────────────────────────┐
│                              │
│         Order Module         │
│                              │
│  我能够做什么?               │
│                              │
│  ✓ 创建订单                   │
│  ✓ 取消订单                   │
│  ✓ 查询订单                   │
│                              │
├──────────────────────────────┤
│                              │
│       内部如何实现?          │
│                              │
│       不需要外部关心          │
│                              │
└──────────────────────────────┘

外部系统只关心:

你能提供什么能力?

而不是:

你内部有多少 Service?

有多少 Repository?

使用什么 ORM?

如何组织 DTO?

这些都应该尽可能隐藏。


十五、Deep Module 并不是“隐藏一切”

当然,Deep Module 也不能被理解成:

把所有代码藏起来。

真正需要隐藏的是:

实现复杂性。

但不能隐藏:

业务语义。

例如:

createOrder(command)

这个接口很好理解。

但是:

executeProcess(command)

虽然也隐藏了实现。

但语义不明确。

所以好的模块接口应该:

隐藏实现复杂性,但暴露清晰的业务能力。

这是非常重要的原则。


十六、AI 时代的 Deep Module 设计原则

我认为可以总结成五条原则。

1. 按能力组织,而不是按技术组织

优先:

order/
payment/
inventory/

而不是:

controller/
service/
repository/

2. 对外接口尽可能少

模块不应该暴露:

OrderRepository
OrderMapper
OrderEntity
OrderAssembler

而应该暴露:

OrderFacade

或者:

OrderModule

例如:

orderModule.createOrder(command);
orderModule.cancelOrder(command);
orderModule.getOrder(id);

3. 内部实现可以复杂

不要为了:

“每个类必须很小”

而无限拆分。

如果几个逻辑:

  • 强相关
  • 总是一起修改
  • 属于同一个业务能力

那么它们可能应该放在一起。


4. 一个任务尽可能只进入一个领域

理想情况下:

修改订单逻辑 → 进入 Order Module。

而不是:

修改订单逻辑 → 搜索整个项目。


5. 模块之间通过能力协作

例如:

Order Module
      │
      │ reserveStock()
      ↓
Inventory Module

而不是:

Order Module
      ↓
直接访问 InventoryRepository

模块应该依赖:

能力。

而不是:

内部实现。


十七、真正的变化:软件架构开始服务于“理解”

过去的软件架构主要解决的是:

如何组织代码。

未来的软件架构可能越来越关注:

如何降低理解成本。

因为未来参与软件开发的,不再只有人。

还有:

AI。

于是架构的目标可能发生变化。

从:

如何让代码运行?

变成:

如何让系统更容易被理解?

从:

如何让开发者维护?

变成:

如何让开发者和 AI 都能够维护?

结语:Deep Module,也许正在成为 AI 时代的重要架构思想

我并不认为 Deep Module 是一个全新的概念。

事实上,它已经存在很多年。

真正发生变化的是:

AI 让我们重新意识到了模块边界的重要性。

当软件越来越复杂。

当代码越来越多。

当 AI 开始参与开发。

一个问题会变得越来越重要:

AI 需要理解多少上下文,才能完成一次修改?

如果答案是:

整个系统。

那么这个系统一定会越来越难维护。

但如果答案是:

一个清晰的领域模块。

那么复杂系统就可以被拆成一个个:

人和 AI 都能够独立理解的自治单元。

这也许就是 Deep Module 在 AI 时代最大的价值:

不是减少代码。

不是减少模块。

而是把复杂性关进正确的边界里。

让系统整体可以很复杂。

但让每一次理解,都尽可能简单。

未来的软件架构,也许会越来越从:

技术分层驱动的软件结构

转向:

领域自治单元驱动的软件结构。

而 Deep Module,将成为这些自治单元最重要的设计原则之一。

好的模块,不是内部没有复杂性。

而是能够承担巨大的复杂性,却不把复杂性转嫁给外部。

这,就是 Deep Module。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐