DDD 值对象:如何在没有大量 SQL 连接的情况下持久保存实体对象?
显然,我错误地认为 DDD 在实用性上与 EAV/CR 类似,但到目前为止,我看到的唯一区别是为每个具有大量联接的实体构建物理表,而不是三个表和大量联接。
这肯定是由于我缺乏对 DDD 的理解。 如何在导入数据时将这些对象物理存储到数据库中,而不需要大量的连接和复杂性?我知道您可以简单地创建输入到存储库中的对象,但是很难训练 Microsoft Sql Server 等工具集成服务器使用您的自定义 C# 对象和框架。也许这应该是我的问题,如何将 DDD ASP.NET C# 框架与 Microsoft SQL Server 集成服务和报表服务结合使用?哈哈。
在 EAV/CR 数据库中,我们可以根据人员类型设置具有不同类别的单个人员表:供应商、客户、买家、代表、公司、管理员等。三个表,一些连接,属性始终为字符串在插入之前进行验证,就像 MVC 中的 ModelValidation 一样,对象接受任何值,但在其有效之前不会保留。
在标准关系模型中,我们过去常常为每种类型的实体创建一个表,并混合城市等冗余数据类型。
使用领域驱动设计,我们使用对象来表示每种类型的实体,使用每种类型的 ValueObject 的嵌套对象,并根据需要使用更多的嵌套对象。根据我的理解,这会为每种实体生成一个表,并为每种信息集(值对象)生成一个表。对于所有这些表,我看到很多连接。我们最终还为每种新的联系人类型创建了一个物理表。显然有更好的方法,所以我在将对象持久保存到数据库的方式上一定是不正确的。
我的供应商如下所示:
public class Vendor {
public int vendorID {get; set;}
public Address vAddress {get; set;}
public Representative vRep {get;set;}
public Buyer vBuyer {get; set;}
}
我的买家:
public class Buyer {
public int buyerID {get; set;}
public Address bAddress {get; set;}
public Email bEmail {get; set;}
public Phone bPhone {get; set;}
public Phone bFax (get; set;}
}
我们是否真的引用了 Vendor.vBuyer.bPhone.pAreaCode 等内容?我认为我们会引用并存储 Vendor.BuyerPhoneNumber,并构建几乎像这些部分的别名一样的对象:Vendor.Address1、Vendor.Address2、Vendor.BuyerPhoneNumber ...等。
Obviously I am wrong in saying that DDD is similar to EAV/CR in usefulness, but the only difference I see so far is physical tables build for each entity with lots of joins rather than three tables and lots of joins.
This must be due to my lack of DDD understanding. How do you physically store these objects to the database without significant joins and complication when importing data? I know you can simply create objects that feed in to your repository, but it's difficult to train tools like Microsoft Sql Server Integration Server to use your custom C# objects and framework. Maybe that should be my question, how do you use your DDD ASP.NET C# framework with Microsoft SQL Server Integration Services and Report Services? LOL.
In an EAV/CR database, we can setup a single Person table with different classes depending on the type of person: Vendor, Customer, Buyer, Representative, Company, Janitor, etc. Three tables, a few joins, attributes are always string with validation before we insert, just like ModelValidation in MVC where the object accepts any value but won't persist until it's valid.
In a standard relational model, we used to create a table for each type of entity, mixing in redundant data types like City.
Using Domain Driven Design, we use objects to represent each type of entity, nested objects for each type of ValueObject, and more nested objects still as necessary. In my understanding, this results in a table for each kind of entity and a table for each kind of information set (value object). With all these tables, I see a lot of joins. We also end up creating a physical table for each new contact type. Obviously there is a better way, so I must be incorrect in how I persist objects to a database.
My Vendor looks like this:
public class Vendor {
public int vendorID {get; set;}
public Address vAddress {get; set;}
public Representative vRep {get;set;}
public Buyer vBuyer {get; set;}
}
My Buyer:
public class Buyer {
public int buyerID {get; set;}
public Address bAddress {get; set;}
public Email bEmail {get; set;}
public Phone bPhone {get; set;}
public Phone bFax (get; set;}
}
Do we really reference things like Vendor.vBuyer.bPhone.pAreaCode? I would think we would reference and store Vendor.BuyerPhoneNumber, and build the objects almost like aliases to these parts: Vendor.Address1, Vendor.Address2, Vendor.BuyerPhoneNumber ... etc.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
真正的答案是将您的 SQL 规范化策略与您的对象相匹配。如果您有大量重复地址并且需要将它们关联在一起,则将数据规范化到单独的表中,从而创建对值对象的需求。
The real answer is to match your SQL normalization strategy to your objects. If you have lots of duplicate addresses and you need to associate them together, then normalize the data to a separate table, thus creating the need for the value object.
您可以将对象序列化为 xml 并将其保存到 Sql Server 中的 xml 列中。毕竟,您正在尝试表示分层数据结构,而这正是 xml 的优势所在。
You could serialize your objects to xml and save it to an xml column in you Sql Server. After all, you are trying to represent a hierarchical data structure, and that's where xml excels.
领域驱动设计的支持者经常建议使数据模型尽可能接近对象模型,但这并不是一条铁定的规则。
如果您在对象关系映射层,用于将数据转换(投影)为对象。
决定如何设计对象并提供对子值的访问实际上是一个单独的问题,您必须根据具体情况来解决。
Vendor.BuyerPhoneNumber
还是Vendor.vBuyer.bPhone.pAreaCode
?答案总是取决于您的具体要求。Domain-driven design proponents often recommend keeping the data model as close to the object model as possible, but it isn't an ironclad rule.
You can still use a EAV/CR database design if you create mappings in your object-relational mapping layer to transform (project) your data into your objects.
Deciding how to design your objects and provide access to child values is really a separate question that you have to address on a case-by-case basis.
Vendor.BuyerPhoneNumber
orVendor.vBuyer.bPhone.pAreaCode
? The answer always depends, because it's rooted in your specific requirements.存储域对象的最佳方法之一实际上是文档数据库。它工作得很好,因为文档的事务边界与聚合根的一致性边界完美匹配。您不必担心 JOIN 或急切/延迟加载问题。不过,如果您应用 CQRS(我在下面写过),那么这并不是绝对必要的。
缺点通常是查询。如果您希望直接查询域对象背后的持久数据,那么您可能会陷入困境。然而,这是 CQRS 旨在为您解决的复杂性,您的应用程序的不同部分执行查询,而不是加载/验证/存储域对象的部分。
您可能有一个复杂的“命令”实现,用于加载域对象,调用它们的行为(记住域对象必须具有行为并封装其数据,否则有变得“贫血”的风险),然后保存它们,甚至可以选择发布有关的事件发生了什么。
然后,您可以使用这些事件来更新其他一些“阅读存储”,尽管您不必这样做。关键是您在应用程序中实现了一个完全不同的垂直切片,它不必担心复杂的对象模型/ORM 业务,而是直接访问数据,准确加载所需的内容并将其返回以显示给用户。
CQRS 并不难,也不复杂。 它所做的只是指示您拥有单独的代码用于:
One of the best ways to store Domain Objects is actually a document database. It works beautifully because the transactional boundary of the document matches perfectly the consistency boundary of the Aggregate Root. You don't have to worry about JOINs, or eager/lazy loading issues. That's not strictly necessary, though, if you apply CQRS (which I've written about below).
The downside is often with querying. If you wish to query directly the persisted data behind your Domain Objects, you can get into knots. However that is a complexity that CQRS aims to solve for you, where you have different parts of your application doing the queries than the parts loading/validating/storing Domain Objects.
You might have a complex "Command" implementation that loads Domain Objects, invokes the behaviour on them (remembering that Domain Objects must have behaviour and encapsulate their data, or else risk becoming "anaemic"), before saving them and optionally even publishing events about what happened.
You might then use those events to update some other "read store", though you don't have to. The point is you have a totally different vertical slice implemented in your application that doesn't have to bother with that complex object model/ORM business, and instead goes straight to the data, loads exactly what it needs and returns it for displaying to the user.
CQRS is not hard, and is not complex. All it does is instruct you to have separate code for: