DDD架构下跨聚合根的User实体管理方案咨询
DDD模块化单体中User跨模块关联的解决方案
别把User实体移到Shared模块
Shared模块的定位是存放通用工具类、全局值对象、跨模块通用接口这类无业务属性的内容,User是独立业务域的聚合根,包含用户注册、权限、状态等专属业务逻辑,移到Shared会直接破坏单一职责原则:
- Shared模块会逐渐臃肿成“大杂烩”,后续User的业务变更会影响所有依赖Shared的模块;
- 违背DDD聚合根的边界设计,User的业务逻辑会被分散到各个模块,难以维护。
用用户ID值对象实现无依赖关联
在Shared模块定义一个UserId值对象,封装ID的格式校验、类型约束等逻辑,所有需要关联用户的模块(Article、后续的资源模块)都用这个UserId来存储创建者/所有者标识,而非直接引用User实体:
- Article模块只依赖Shared的
UserId,完全不依赖User模块,符合六边形架构的依赖倒置规则; - User模块也无需依赖任何业务模块,专注自身业务逻辑。
跨模块数据交互用端口-适配器解耦
如果业务需要从Article模块获取用户的展示信息(比如作者名称),绝对不能直接调用User模块的服务,而是用六边形架构的端口-适配器模式:
- 在Article模块内部定义用户信息查询端口(比如
UserInfoQueryPort),这个端口属于Article的输入端口,仅定义Article需要的查询能力; - 在User模块实现这个端口的适配器(比如
UserInfoQueryAdapter),适配器内部调用User模块的仓储或服务获取数据; - 通过依赖注入把适配器注入到Article模块,Article只依赖自己定义的端口,完全解耦于User模块。
示例代码(Java):
// Shared模块:UserId值对象 public record UserId(String value) { public UserId { if (value == null || value.isBlank()) { throw new IllegalArgumentException("用户ID不能为空"); } } } // Article模块:用户信息查询端口 public interface UserInfoQueryPort { String getUserNameById(UserId userId); } // Article模块:Article实体 public class Article { private final String id; private final UserId creatorId; private String title; public Article(String id, UserId creatorId, String title) { this.id = id; this.creatorId = creatorId; this.title = title; } // 通过端口获取作者名称 public String getCreatorName(UserInfoQueryPort queryPort) { return queryPort.getUserNameById(this.creatorId); } } // User模块:端口适配器实现 public class UserInfoQueryAdapter implements UserInfoQueryPort { private final UserRepository userRepo; public UserInfoQueryAdapter(UserRepository userRepo) { this.userRepo = userRepo; } @Override public String getUserNameById(UserId userId) { return userRepo.findById(userId.value()) .map(User::getUserName) .orElseThrow(() -> new RuntimeException("用户不存在")); } }
跨模块业务规则的处理
如果有跨模块的业务规则(比如仅激活用户可创建文章),不要在Article模块内判断用户状态,推荐两种方案:
- 跨领域服务:定义一个
ArticleCreationService,同时依赖User模块的状态查询端口和Article模块的创建端口,在服务内完成用户状态校验,再触发文章创建; - 事件驱动:User模块发布用户状态变更事件(比如
UserActivatedEvent),Article模块订阅事件并缓存必要状态,或者在创建时通过User端口实时查询状态。
内容的提问来源于stack exchange,提问作者Jazi
相关产品推荐
相关产品推荐

