月度记录-2026-01月

12 阅读5分钟

1. DDD 落地与微服务拆分原则

在基于 DDD 的微服务架构中,服务拆分的核心不是技术层,而是业务边界。绝不能按照技术分层(Application / Domain / Infrastructure)来拆分微服务。这种拆分方式会导致业务调用被迫跨服务完成,最终形成低效的分布式单体,并引入严重的网络延迟、事务一致性和系统复杂度问题。

a. 拆分的核心原则:限界上下文(Bounded Context)

  • 限界上下文是业务语义一致性的边界
  • 一个限界上下文通常对应一个独立的业务能力
  • 微服务应围绕限界上下文进行拆分,而非围绕技术层

限界上下文是微服务拆分的逻辑依据,而非代码结构本身

b. 微服务内部仍采用分层架构

在单个微服务(限界上下文)内部,依然遵循清晰的分层设计:

  • Application Service:流程编排、用例驱动
  • Domain Service / Entity / Aggregate:业务规则与核心模型
  • Infrastructure:技术实现细节(DB、MQ、RPC)

分层是服务内部的设计手段,而不是服务拆分的依据

c. 拆分策略:宁大勿小,演进式拆分

  • 初期应优先保证业务内聚,避免过度拆分
  • 微服务拆分成本高,而合并成本更高
  • 可以先在单体中通过限界上下文进行清晰的模块隔离,再逐步演进为独立服务

拆分应该是架构演进的结果,而不是一开始的目标

2. DDD Factory 是领域模型的“守门人”

Factory 的职责不是“替代 new”,而是“保证领域对象诞生即正确”。再狠一点说:如果一个校验不在 Factory 或 Entity 中,那你其实是在允许系统中存在“非法对象”。Factory 不负责“业务流程”,Factory 只负责**“构造一个符合领域不变式(Invariant)的对象”**。

与“对象合法性直接相关的业务校验”可以、也应该放在 Factory 中。

与“业务流程、决策、跨聚合规则”相关的校验,不应该放在 Factory 中。

Factory 不应该负责跨聚合的业务校验,例如:- 校验用户是否被冻结 - 校验库存是否充足 - 校验是否已存在未支付订单

这些都 依赖外部系统或其他聚合状态。 这不是“对象是否能存在”,而是“此时此刻是否允许这么做”。结论:不属于 Factory。

3. 调用别的服务的接口,比如调用dubbo接口,DIP依赖倒置

应该在领域层下定义一个防腐层ACL,放接口,使用当前服务的业务术语定义。

infra层实现接口,调用具体的接口。

接口定义:领域层

是否调用,调用顺序:应用层

技术调用实现:基础设施层

调用本身是否是业务规则:取决于语义

4. 怎么看DDD是否成功

首先从人脑信息输入和处理信息看,感官输入:1Gbps;有意识思考:10bps。差距相当巨大。引出7+-2原则,人的短时记忆一次处理5-9个信息元。所以说DDD并不能让人脑处理复杂度的能力提升,而是为了给人类大脑打补丁。

屏蔽胜过处理:计算机通过增加频率、核心处理复杂度。而人类大脑必须通过忽略处理复杂度。DDD就是一套忽略规则。上层不需知道下层细节。减少了大脑需要实时渲染的信息量。降低认负荷,分离关注点。让大脑可以切单线程模式。软件复杂性不是被“解决”的,而是被“隔离”的。DDD,就是隔离复杂性的工程化方法。

优秀的架构师,本质上不是性能优化专家,也不是技术堆叠专家,而是“认知负荷管理专家”。他通过整洁代码、设计模式、DDD 战略建模,刻意营造一个环境,让人类大脑那可怜的 ~10bps 意识带宽,还能在持续膨胀的系统复杂度中勉强运转。顺着这个逻辑,会自然推出一个残酷结论:如果 DDD 不能明显降低开发者的即时认知负荷,那它就是失败的 DDD。任何增加“脑内跳转成本”的设计,都是反 DDD 的。

5. @ThriftField(requiredness=REQUIRED)

该字段在 Thrift 协议层面是“强制存在的字段”,序列化时必须写出,反序列化时必须读到。只要使用 REQUIRED,你就基本放弃了协议的向前 / 向后兼容性。

99% 的业务字段,不应该用 REQUIRED。REQUIRED 不是“更严谨”,而是“把演进成本提前透支”。在分布式系统里,它几乎总是错误的选择。

6. JDK21虚拟线程

JDK 21 的虚拟线程之所以“快”,不是因为单次执行更快,而是因为“阻塞的成本被从 OS 层降维到了 JVM 层”。

虚拟线程解决的不是 CPU 计算慢,而是 “等 I/O 时浪费太贵”。虚拟线程 ≠ 更快的 OS 线程,虚拟线程是:JVM 管理的用户态线程(User-mode Thread)。

OS 线程(平台线程)的硬成本

传统 Java 线程 = 操作系统线程(1:1 映射)

代价包括:

  • 线程栈内存:默认 ~1MB / 线程
  • 上下文切换:内核态 ↔ 用户态
  • 调度权在 OS

结果:

  • 几千线程 → 内存压力
  • 几万线程 → 调度灾难
  • 阻塞 I/O → 线程“空等”

Java 第一次把“并发调度权”从 OS 手里拿回 JVM。虚拟线程的成功级别,达不到 Java 8 Lambda;但它对 Java 服务器端的“结构性影响”,反而更深。