DDD聚合设计中封装与业务规则的冲突及C#解决方案咨询
DDD中聚合实体属性修改的权限与业务逻辑平衡问题
在领域驱动设计(DDD)中设计聚合和实体时,应仅向使用者暴露聚合的公共属性与方法。但修改部分属性时需执行复杂业务逻辑,例如修改User聚合的Email属性时,需校验该邮箱未被其他用户注册,这涉及数据库查询。
将仓储(或仓储接口)嵌入聚合方法属于不良实践,会引入进程外依赖,因此需将该逻辑置于DomainService或AppService中。但这会导致属性需设为internal或public,存在被误修改、绕过服务中业务规则的风险:
public class User : IAggregateRoot { public int Id { get; } public string Email { get; set; } private User(int id, string email) { Id = id; Email = email; } } public class UserService { private IUserRepository repository; public UserService(IUserRepository repository) { this.repository = repository; } public async Task ChangeEmail(int userId, string email) { var exist = await repository.GetByEmailAsync(email); if (exist != null) throw new DomainException("Email is already in use"); var user = await repository.GetByIdAsync(userId); user.Email = email; await repository.SaveAsync(user); } }
理想状态是属性仅允许私有修改,外部仅能通过ChangeEmail方法访问:
public string Email { get; private set; }
若C#支持友元类,可让服务访问聚合或实体的私有属性与方法,即可解决该问题;若允许程序集循环引用(每个方块为独立项目),也可解决,但C#禁止循环依赖。
尝试使用嵌套类访问私有成员,但嵌套类无法同时关联聚合及所属实体(如Order聚合与OrderItem实体无法共用同一嵌套类)。虽可采用类似方案,但会导致项目数量及依赖关系激增,若支持循环依赖则可简化。
内容的提问来源于stack exchange,提问作者Alexandr Kubit
相关产品推荐
相关产品推荐

