沉默是金,总会发光
大家好,我是沉默
在日常开发中,我们经常听到“DDD”这三个字母。
有人说它高级,有人说它难懂。
你可能也看过一堆长篇大论的 DDD 讲解,读完只觉得:“好像很厉害,但我真不会用。”
今天咱们用一个通俗易懂的案例告诉你:
DDD(领域驱动设计)到底是啥?它和传统开发有什么区别?怎么用到真实项目里?
DDD听上去高大上,其实落地也能很接地气。
**-**01-
DDD 是什么?
DDD,全称 Domain-Driven Design,领域驱动设计。
一句话总结:
用代码还原业务本质,而不是为了实现功能堆 if-else。
它的核心思想是:
把业务规则写到“懂业务”的对象里,让代码成为业务的镜子。
我们传统是:
PRD怎么写 → 程序员脑补 → controller/service/dao 东拼西凑。
而DDD更像是:
业务方讲清业务 → 画出领域模型 → 代码结构天然映射业务流程。
**-**02-
DDD和传统代码的差距
假设我们做一个用户注册功能,需求如下:
-
用户名唯一
-
密码必须符合复杂度要求
-
注册成功后记录日志
传统开发这么写:
@Controller
public class UserController {
public void register(String username, String password) {
// 密码校验
// 查询用户名是否存在
// 保存数据库
// 写日志
}
}
业务、数据、流程,全都混在一起!
有人说:“我已经分层啦!”
public class UserService {
public void register(User user) {
ValidationUtil.checkPassword(user.getPassword());
if (userRepository.exists(user)) { ... }
userDao.save(user);
}
}
嗯,分层是有了,但业务规则仍然乱飞:
-
密码规则 → 工具类
-
唯一校验 → service 层
-
数据存储 → dao
DDD怎么重写注册功能?
我们来一把“代码内聚”操作,把业务规则放进 User 领域对象:
public class User {
public User(String username, String password) {
if (!isValidPassword(password)) {
throw new InvalidPasswordException();
}
this.username = username;
this.password = encrypt(password);
}
private boolean isValidPassword(String password) {
// 复杂度规则
}
}
这里,User 不再只是数据容器(贫血模型),而是能“自己判断密码是否合规”的“业务对象(充血模型)”。
这样简单一改,谁再说“DDD只是分层”?
**-**03-
DDD三大关键设计
DDD不仅仅是让对象有行为,它还有三大核心概念:
聚合根(Aggregate Root)
聚合根是啥?一句话概括:
“你必须通过我来操作我手下的对象。”
比如一个用户(User)有多个收货地址(Address):
public class User {
private List<Address> addresses;
public void addAddress(Address address) {
if (addresses.size() >= 5) {
throw new AddressLimitExceededException();
}
addresses.add(address);
}
}
用户自己控制地址添加,防止外部乱搞。
领域服务 vs 应用服务
-
领域服务:负责跨实体的核心业务规则
-
应用服务:负责流程编排,调用领域服务、消息通知等
public class TransferService {
public void transfer(Account from, Account to, Money amount) {
from.debit(amount);
to.credit(amount);
}
}
public class BankingAppService {
public void executeTransfer(Long fromId, Long toId, BigDecimal amount) {
Account from = repo.find(fromId);
Account to = repo.find(toId);
transferService.transfer(from, to, new Money(amount));
mq.send(new TransferEvent(...));
}
}
举个栗子:
领域服务 = 决策者
应用服务 = 指挥官
领域事件(Domain Event)
当一个重要业务动作发生时,比如“用户注册成功”,就可以发布领域事件:
public class User {
public void register() {
// 注册逻辑
events.add(new UserRegisteredEvent(this.id));
}
}
领域事件 = “系统内的广播”,用来驱动其他逻辑如发送短信、积分奖励等。
**-**04-
电商下单案例对比
传统开发:业务散、逻辑乱
public class OrderService {
public Order createOrder(Long userId, List<ItemDTO> items, Long couponId) {
// 校验库存
// 计算价格
// 应用优惠券
// 保存订单
}
}
业务规则到处是:
-
库存逻辑 → Service
-
优惠券逻辑 → Util
-
总价计算 → Controller 或工具类
一改需求,Service 就成了“代码考古现场”。
DDD版本:业务逻辑高度内聚
public class Order {
public Order(User user, List<OrderItem> items, Coupon coupon) {
items.forEach(item -> item.checkStock());
this.totalAmount = items.stream()
.map(OrderItem::subtotal)
.reduce(Money.ZERO, Money::add);
if (coupon != null) {
validateCoupon(coupon, user);
this.totalAmount = coupon.applyDiscount(this.totalAmount);
}
}
private void validateCoupon(Coupon coupon, User user) {
if (!coupon.isValid() || !coupon.isApplicable(user)) {
throw new InvalidCouponException();
}
}
}
改需求只需要调整 validateCoupon(),业务边界清晰明了。
**-**05-
什么时候该用DDD?
不要盲目上DDD,它不是银弹!
适合使用的场景:
-
业务逻辑复杂(如电商、金融、供应链)
-
需求频繁变更(如互联网产品、增长业务)
不推荐使用的场景:
-
简单 CRUD(如数据管理后台、CMS)
-
一次性项目(成本高,收益小)
一句话:
当你发现业务变更时,只改领域层就能搞定,那你真的把DDD用对了!
总结:
DDD的魅力在于:
让代码的结构贴近业务逻辑,让系统更容易理解、更容易维护。
-
把规则写进对象,而不是 scattered everywhere
-
把流程封装成服务,而不是 controller+util 拼图游戏
-
把变化记录成事件,而不是“流程散记”
DDD不追求“写得快”,而追求“改得稳”。
如果有一天,你看代码就像看 PRD,那就是你 DDD 落地的时刻!
热门文章
**-**06-
粉丝福利
点点关注,送你 Spring Cloud 微服务实战,如果你正在做项目,又或者刚准备做。可以仔细阅读一下,或许对你有所帮助!