design-patterns - 域驱动实现 - 更新聚合根中的单个属性
问题描述
我是 DDD 的新手,我想对我在实施它时面临的一些挑战提出一些建议。
我正在使用 Typescript 开发应用程序。数据保存在关系数据库中。我们没有遵循 CQRS 模式,我们的读取和写入发生在同一个数据库中。
假设我有一个User
大致如下所示的聚合,
class User extends AggregateRoot {
id: number;
phone: Phone;
email: Email;
address: Address;
private constructor(id, phone, email, address){
//setting the values
}
public static create(props) {
return new User({...props});
}
public static update(props) {
return new User({...props});
}
}
在这里,Phone
and Email
are ValueObjects
and Address
is an Entity
.
class Phone extends ValueObject {
phNumber: string;
private constructor( ph ) {
phNumber = ph;
}
public static create(ph){
//do validations
return new Phone(ph);
}
}
该类Email
也类似于Phone
.
现在,一旦在控制器中接收到更新电话请求,请求就会被转发到User Service
层,服务将大致如下所示,
public updatePhone( updatePhNoDto ) {
const userEntity = userRepository.getUser(updatePhNoDto.userId);
const userModel = User.update({
id: updatePhNoDto.userId,
phone: Phone.create(userEntity.phone),
email: Email.create(userEntity.email),
address: Address.create(userEntity.address)
});
userRepository.updateUser(userModel)
}
在这里,每次用户请求更新电话号码时,我都会从 RDBMS 中获取用户数据,并对所有已验证的字段进行所有验证,然后调用方法User.update()
。所以,这是我的问题:
- 不确定上述方法是否正确,因为我正在验证我已经验证过的东西,并且可能是不必要的数据库调用。因此,请向我建议处理此类情况的最佳实践,即要求更新单个或仅几个字段。
- 用户可以独立于他的其他信息更新他的地址。那么,
Address
实体应该是独立的Aggregate Root
吗?如果是,如果在单个 http-request 中同时请求更新 UserInfo 和 Address,应该如何处理? - 聚合根在删除中的作用是什么?应该如何在其中建模?
如果您在设计中发现任何其他缺陷,请告诉我。
谢谢!
解决方案
就像控制器有一个 UpdatePhone 端点一样,用户将有一个 UpdatePhone 方法,它只验证和更新电话号码。用户 AR 还将具有 UpdateEmail、UpdateAddress 等。
如果用户可以在前端一次更改多个用户属性,您可以使用控制器来解决这个问题。您将在控制器上有一个 UpdateUser 端点,该端点将决定哪些已更改,哪些未更改,然后在用户上调用所有必要的方法。一些伪代码:
If (PhoneInfoUpdated) User.UpdatePhone({用户提交的电话字段}); If (EmailInfoUpdated) User.UpdateEmail({用户提交的电子邮件字段}); If (AddressInfoUpdated) User.UdateAddress({用户提交地址信息});
(为了简洁起见,您可能只是在帖子中删除了它,但请记住这里有 2 个级别的数据验证。控制器验证日期类型,这样如果您期望整数,您实际上会得到整数,日期是日期、电话号码和电子邮件是正确的格式等。然后在 User.UpdateWhatever 方法中,您验证是否满足业务规则,例如电子邮件地址不是现有地址的副本等)
我不明白地址如何在不归用户所有的情况下拥有自己的生命,但如果这是您的业务案例,那么它应该是 AR。因此,要更改地址,您应该有一个单独的地址 API 端点来执行正确的操作,而不是尝试通过用户端点发送它。如果前端直接调用 API,或者如果您使用 MVC 控制器接收回发,然后可以在 AR 上调用适当的 API 或适当的方法,则前端应该决定要调用的正确端点。
至于删除,我从来都不喜欢实际删除,所以我建议添加一个 Active 标志(或 Deleted 标志,具体取决于您处于无休止辩论的哪一边)。无论您是实际删除还是只是设置一个标志,您都应该有一个 User.Delete 方法。如果您真的要删除该行,我更喜欢将其作为 User 类的静态方法,这样您就不必检索用户然后删除它。如果您使用标志,则 Delete 方法应该在类上是公共的,因为它实际上只是像设置任何其他属性一样设置属性。
推荐阅读
- java - 访问 Set 中的元素
- sql - R 从 SQL 查询中截取字符串结果
- mysql - “列数与行中的值不匹配。” 缺少自动生成的 t_stamp
- r - 如何从 r 的一列中排除某些时间?
- c++ - 定义多个C++类成员函数
- sql - 带有错误 ORA-00904 的 Oracle 11g 更新语句
- c++ - C++ NetBeans“找不到其声明的标识符。” 错误
- google-cloud-platform - 编写GCP演练教程时如何在cloud shell中指定图像路径?
- python - 最接近某个值的元素(逐元素,numpy 数组)
- javascript - 防止 Bootstrap Modal 在单击外部或按 Escape 时消失