要向存储库添加订单,我通常会看到人们在没有Id的情况下实例化订单,然后让存储库完成对象:
class OrderRepository { void Add(Order order) { // Insert order into db and populate Id of new order } }
我喜欢这种方法的是你将Order实例添加到OrderRepository.这很有道理.但是,订单实例没有Id,并且在存储库的使用者的范围内,对我来说,订单没有Id也没有意义.我可以将OrderRequest定义为订单的实例并将其添加到存储库,但这感觉就像从橙色中导出苹果然后将其添加到橙色列表中.
或者,我也看到过这种方法:
class OrderRepository { Order AddOrder(Customer customer) // It might be better to call this CreateOrder { // Insert record into db and return a new instance of Order } }
我喜欢这种方法的是没有Id的订单是未定义的.存储库可以创建数据库记录并在创建和返回订单实例之前收集所有必需的字段.这里有什么味道,你实际上从未向存储库添加订单实例.
无论哪种方式都有效,所以我的问题是:我是否必须接受这两种解释中的一种,或者是否有最佳实践来模拟插入?
我发现这个答案类似,但对于价值对象:
how should i add an object into a collection maintained by aggregate root.当涉及到一个价值对象时,没有混淆,但我的问题涉及一个从外部来源(自动生成的数据库ID)得到识别的实体.
其余选项取决于域逻辑.如果域逻辑规定没有ID的订单没有意义,则ID是Order的必需不变量,我们必须对其进行建模:
public class Order { private readonly int id; public Order(int id) { // consider a Guard Clause here if you have constraints on the ID this.id = id; } }
请注意,通过将id字段标记为只读,我们已将其设为不变量.我们无法为给定的Order实例更改它.这完全符合Domain-Driven Design的实体模式.
您可以通过将Guard子句放入构造函数来进一步强制执行域逻辑,以防止ID为负或零.
到目前为止,您可能想知道这可能如何与数据库中自动生成的ID一起使用.嗯,事实并非如此.
没有好办法确保提供的ID尚未使用.
这有两个选择:
>将ID更改为Guid.这允许任何呼叫者为新订单提供唯一ID.但是,这要求您也使用Guids作为数据库键.
>更改API,以便创建新订单不会采用Order对象,而是按照您的建议采用OrderRequest – OrderRequest可能与Order类几乎相同,减去ID.
在许多情况下,创建新订单是一种在任何情况下都需要特定建模的业务操作,因此我认为没有问题可以做出这种区分.尽管Order和OrderRequest在语义上可能非常相似,但它们甚至不必在类型层次结构中相关.
我甚至可能会说它们不应该相关,因为OrderRequest是一个Value Object而Order是一个实体.
如果采用这种方法,AddOrder方法必须返回Order实例(或至少ID),否则我们无法知道刚创建的订单的ID.这导致我们回到CQS违规,这就是为什么我倾向于选择Guids for Entity ID.