public class Order
{
List<OrderItem> Items {get; private set;}
public AddItem(OrderItem item)
{
//логика добавления
items.Add(item);
}
}
遵循 DDD 方法论,所有领域逻辑都位于领域内部,不会被取出到单独的服务中。问题是如何在不使用 ORM或使用 microOrm - Dapper 的情况下保存对聚合根的更改。你好吗?
按照 DDD 的原则,storages 负责存储实体,它们也是允许你读写聚合的存储库。在这种情况下,订单存储库必须能够读取和写入订单聚合,其中包括订单项。
由于实体的存储方式很可能会发生变化,因此我们在域中声明的不是存储库的实现,而是接口:
该接口的实现将驻留在应用程序的基础设施层(Eric Evans 的术语)。不是DDD,这一层叫做数据访问层。按照数据隐藏的原则,订单实体的内部数据是无法从基础设施层到达的。
示例:一个实体
Order有一个属性CreatedAt,即创建日期。根据域的规则,这个属性是只读的,即有getter方法,没有setter方法。当订单存储库从数据库加载数据时,它必须设置所有属性的值,包括CreatedAt. 但它不能那样做,因为它不能改变只读属性的值。GoF 中描述了类似的问题,其中使用Memento ( Keeper )模式来解决它。它允许您保存对象的状态并在以后恢复它。在经典方案中,状态以无法分析和理解的形式存储,但我们需要其他东西。
如果我们使用 DBMS,我们希望状态采用一种便于存储在 DBMS 中的形式。为此,我们为实体引入了 DTO。这些对象也位于域层中,因为它们是基础设施层接口的一部分。
在这种形式中,存储库从数据库加载数据,将其转换为 DTO 对象,然后从 DTO 对象创建域对象。要么获取域实体,将其转换为 DTO 对象,然后保存。
让我再修正一下:领域层描述了领域实体(
Order, ),它分别描述了需要在基础设施层( )中实现OrderItem的存储库接口( ) ,最后,它描述了 DTO 对象(, )包含实体 ( ) 想要永久存储的所有字段。实体 ( ) 包含逻辑并隐藏实现,DTO 仅包含数据而没有逻辑。IOrderRepositoryAdoOrderRepositoryOrderDtoOrderItemDtoOrderOrder存储库实现能够与 DTO 对象一起工作,特别是,它们可以向数据库发送读取请求并将结果写入 DTO 对象,因为 DTO 对象非常简单。但是他们如何将 DTO 对象转换为实体,反之亦然?在我上面提供的代码示例中,我写到这神奇地发生了。
现在是弄清楚具体方法的时候了。域本身的对象,例如
Order. 但它们有一个主要功能 - 这些是主题区域的订单。添加第二个功能将违反单一职责原则。我们需要一个单独的类来访问实体状态Order。但是一个类不需要知道另一个类的实现细节。嵌套此类时除外。
内部类被描述为公共的,因此基础设施级别的存储库实现可以访问它。
使用这种方法,存储库的实现方式无关紧要:通过像实体框架这样的大型 ORM,通过简单的 microORM Dapper,或通过 ADO。
您需要小心聚合对象,例如 collections
OrderItem/OrderItemDto。Inside 我们有一个 collectionOrderItemDto, outsideOrderItem,它们必须匹配。这个任务不是很困难。最后,我想说明域对象和 DTO 之间的区别,因为有时看起来它们之间有太多共同点,以至于可以放弃 DTO。有时确实有很多共同点,但有时却没有。
如果我们有一个有密码的用户实体,那么在主题区域,它看起来像这样:
DTO 对象因为它看起来完全不同。
如您所见,DTO 对象提供对原始数据的访问,在本例中为密码哈希。域对象隐藏哈希,不仅用于写入,而且用于读取,以避免可能的安全问题。它提供了告诉我们如何使用对象
ValidatePassword的方法。ChangePasswordUser这就是为什么 DTO 对象和主题领域的实体,尽管有一些重复的领域,但属于不同的层次并具有不同的目的。
有一些关于如何设计 DTO 的技巧。理想情况下,它们应该被制作成可以立即在实体框架、NHibernate 或 Dapper 中使用。这意味着您可以使用名称空间中的外键、导航属性和类似属性。这不是必需的,但会使存储库的实现更容易。另一方面,最好不要依赖 ORM 特定的属性和解决方案,例如. 通过这种方式,您可以让 DTO 与特定的 ORM 分离,并可以快速从 EF/SQL 迁移到 MongoDB 或 Redis。
[Key][TableName]System.Components.Annotations[Index]最后,经过无尽的理论,我将对这个问题给出一个具体的答案——在不使用ORM的情况下,究竟如何保存对聚合根的更改。我将添加方法
AdoOrderRepository.Update:按照 DDD,我们不仅要保存聚合的根,还要保存所有聚合的实体,这就是我们在这个方法中所做的。我们创建一个事务来保证变化的一致性。代码很繁琐,但很简单。为了摆脱笨重,你可以使用 Dapper。