我是 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
和Email
是ValueObjects
,Address
是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请求中同时请求更新UserInfo和Address,应该如何处理?如果您发现设计中存在任何其他缺陷,请告诉我。
谢谢!
就像控制器有一个 UpdatePhone 端点一样,用户将有一个 UpdatePhone 方法,仅验证和更新电话号码。 User AR还会有UpdateEmail、UpdateAddress等
如果用户可以在前端一次更改多个用户属性,则可以使用控制器来解决这个问题。 您将在控制器上有一个 UpdateUser 端点,该端点将决定哪些内容已更改、哪些内容未更改,然后调用 User 上的所有必要方法。 一些伪代码:
If (PhoneInfoUpdated) User.UpdatePhone({user submitted phone fields});
If (EmailInfoUpdated) User.UpdateEmail({user submitted email field});
If (AddressInfoUpdated) User.UdateAddress({user submitted address info});
(为了简洁起见,您可能只是在帖子中删除了它,但请记住这里有 2 个级别的数据验证。控制器验证数据类型,这样如果您期望整数,您实际上会得到整数,日期是日期,电话号码和电子邮件的格式正确,等等。然后在 User.UpdateWhatever 方法中,您验证是否满足业务规则,例如电子邮件地址不是现有地址的重复等)
我不明白地址如何在不被用户拥有的情况下拥有自己的生命周期,但如果这是您的业务案例,那么它应该是 AR。 因此,要更改地址,您应该有一个单独的地址 API 端点来执行正确的操作,而不是尝试通过用户端点发送该地址。 如果前端直接调用 API,或者如果您使用 MVC,则控制器接收回发,然后可以调用正确的 API 或 AR 上的适当方法,前端应该决定要调用的正确端点。
至于删除,我从来不喜欢实际删除,所以我建议添加一个“活动”标志(或“删除”标志,具体取决于您站在这场无休止的辩论的哪一边)。 无论您实际删除还是只是设置一个标志,您都应该有一个 User.Delete 方法。 如果您确实要删除该行,我更喜欢将其作为 User 类上的静态方法,这样您就不必检索用户然后删除它。 如果您使用标志,则删除方法应该在类上公开,因为它实际上只是像任何其他属性一样设置属性。