如何正确构建富领域模型?关于依赖与职责的技术问询
一、不依赖外部服务的情况下,富领域模型如何与外部系统交互?
核心思路是让领域模型只负责生成领域事件,不处理外部交互,把外部操作的职责交给外层的应用层或基础设施层,具体做法如下:
在
User模型内部维护一个私有未发布事件列表(比如_unpublishedEvents),每个业务方法执行完成后,生成对应的领域事件并添加到这个列表中。比如:- 执行
TransferMoney()后,生成MoneyTransferredEvent,包含转账双方ID、金额等信息 - 执行
SendFriendInvitation()后,生成FriendInvitationSentEvent,包含邀请发起方、接收方ID等信息
- 执行
模型对外提供获取未发布事件的方法(比如
GetUnpublishedEvents()),以及标记事件已发布的方法(比如MarkEventAsPublished()),但这些方法只负责事件的管理,不涉及任何外部服务调用。应用层在调用模型的业务方法后,主动提取这些未发布事件,再调用对应的外部服务(数据库、消息队列、审计系统等)完成交互。示例代码如下:
// 应用层代码 var userA = userRepository.Get(userIdA); var userB = userRepository.Get(userIdB); userA.TransferMoney(userB, transferAmount); // 处理模型生成的事件 foreach (var evt in userA.GetUnpublishedEvents()) { if (evt is MoneyTransferredEvent transferEvent) { // 由仓储层处理数据库持久化 userRepository.Save(userA); userRepository.Save(userB); // 调用审计服务记录转账日志 auditService.LogTransfer(transferEvent); } // 标记事件已处理,避免重复执行 userA.MarkEventAsPublished(evt); }
这种方式下,User模型完全不需要依赖任何外部组件,只专注于自身的领域规则和状态变更,外部交互逻辑完全剥离到外层,符合富领域模型“独立于服务”的要求。
二、富领域模型是否违反单一职责原则?
答案是不违反,原因如下:
单一职责原则的核心是“一个类只应该有一个引起它变化的原因”,富领域模型的职责是封装领域实体的核心业务规则和状态。User模型中的TransferMoney()、SendFriendInvitation()、AcceptFriendInvitation()等方法,都是围绕用户这个领域实体的核心业务行为展开的,所有这些行为的变化都只源于用户领域规则的调整(比如转账限额修改、好友邀请规则变更),不存在多个独立的变化原因。
反观贫血模型(只存储数据,业务逻辑散落在服务层),反而更容易违反单一职责:比如一个UserService可能同时处理转账、好友管理、用户资料修改等多个不相关的逻辑,任何一个逻辑的规则变化都需要修改这个服务,才是真正违反了单一职责。
内容的提问来源于stack exchange,提问作者Szyszka947

