问题标签 [ddd-repositories]
For questions regarding programming in ECMAScript (JavaScript/JS) and its various dialects/implementations (excluding ActionScript). Note JavaScript is NOT the same as Java! Please include all relevant tags on your question; e.g., [node.js], [jquery], [json], [reactjs], [angular], [ember.js], [vue.js], [typescript], [svelte], etc.
domain-driven-design - 像 ddd 构建块一样的域对象的命名,例如存储库
当在域模型中跨越概念时,其中存在具有名称且听起来像对象但与 5 个主要 DDD 构建块之一的职责重叠的东西,命名该对象和/或处理设计的最佳实践是什么?在实际实现中可能会或可能不会包含该名称或短语?
举一个更具体的例子,假设我们正在本着 DDD 的精神设计一个时间跟踪应用程序,并且遇到了领域专家称之为“时间日志”的东西,它应该是包含打卡和对应的日志所有员工的打卡时间。
有了这些信息,我最初的想法是,如果编写了一个名为 TimeLog 的类,它允许查询现有的时间条目以及持久化新的或修改过的时间日志条目,那么这样的类实际上是在扮演 DDD 存储库的角色。为简单起见,假设经过各种讨论和重构,我们得出一个结论,每次日志条目本质上都是它自己的聚合根,因此需要相应的存储库。
现在我们可以选择将我们的存储库命名为TimeLog,这似乎更符合通用语言的 DDD 概念,或者我们可以将其称为 TimeLogEntryRepository这似乎符合在它们查询/保留的聚合根之后命名存储库的更一般约定。我更倾向于使用 TimeLog 的想法,因为它更能描述它在领域模型中所扮演的实际角色,这反过来应该有助于将设计传达给领域专家。另一方面,使用 TimeLogEntryRepository 的选择遵循现有的 DDD 约定,因此会使开发人员更容易遵循设计。折衷方案也可以是使用 TimeLog 命名,但让所有存储库实现 IRepository 接口或从公共存储库基类继承,以帮助开发人员定位和区分存储库类与构成域模型的其他存储库类。
在这种情况下,最佳做法是什么?我可以看到服务可能会发生相同类型的问题,因为它们是开发人员通常使用“服务”后缀命名的典型 DDD 构建块的另一部分,例如在 SomeComplexActivityService 中,但对于实体和值对象,这确实不是问题. 我特别有兴趣看看其他人可能会说那些拥有更多 DDD 经验的人。
linq - 我应该将 DDD 聚合根存储库与 EF 4.1 + LINQ 一起使用吗?
我已经阅读了 DDD Evans,并且正在尝试使用 C# 和 Entity Framework 4.1 + LINQ 进行聚合根存储库设计。
但是,我担心发送到数据库的实际查询。我正在使用 SQL 2008 R2,并运行 SQL Profiler 来检查数据库响应 LINQ 代码所做的工作。
考虑一个包含 Person 和 EmailAddress 的简单 2 实体设计。一个人可以有零到多个电子邮件地址,一个电子邮件地址必须只有一个人。Person 是聚合根,因此不应有电子邮件地址的存储库。电子邮件地址应从个人存储库中选择(根据 DDD Evans)。
为了比较,我确实为电子邮件地址设置了一个临时存储库。以下代码行:
...根据分析器执行一个干净的 SQL 查询:
我可以使用以下代码从人员存储库中选择电子邮件:
这让我在运行时获得了相同的实体,但在 SQL Profiler 中显示了不同的命令:
除了上述从 Person 中选择的查询之外,还有许多“RPC:Completed”事件,一个对应于 DB 中的每个 EmailAddress 行:
我的测试数据库在 dbo.EmailAddress 中有 14 行,并且有 14 个不同的 RPC:Completed 调用,每个调用都有不同的 @EntityKeyValue1 值。
我假设这对 SQL 性能不利,因为随着 dbo.EmailAddress 表获得更多行,将在 db 上调用更多这些 RPC。是否有另一种更好的方法来使用带有 EF 4.1 + LINQ 的 DDD 聚合根存储库?
更新:已解决
问题是 All 属性正在返回一个IEnumerable<TEntity>
. 将其更改为 后IQueryable<TEntity>
,LINQ 启动并一次性选择了整个 Person + Emails。但是,在从 All 返回 IQueryable 之前,我必须链接 .Include(p => p.Emails)。
orm - 存储库的预期行为
我正在编写一个 ORM,但不确定存储库的预期行为,或者更准确地说,不确定存储库和工作单元之间的边界。据我了解,存储库可能如下所示:
根据福勒(PoEAA,第 322 页):
存储库在域和数据映射层之间进行调解,就像内存中的域对象集合一样。[...] 可以将对象添加到存储库中或从存储库中删除,就像它们可以从简单的对象集合中一样。
这意味着以下测试应该可以工作(假设我们已经有一个 Person 持久化,其姓氏是 Fowler):
这意味着当映射到数据库时,即使没有在某处调用显式 save() 方法,Person 模型也必须由 Repository 自动持久化,以便下一个查询返回正确的集合,而不包含原始 Person。
但是,这不是工作单元的角色,决定将哪个模型持久化到数据库,以及何时持久化?
在上述实现中,Repository 必须决定在接收到另一个 find() 调用时将之前检索到的 Person 持久化,以便结果与修改一致。但是,如果没有发出其他 find() 调用,则模型根本不会被隐式持久化。
在工作单元的上下文中,这不是一个真正的问题,因为我们可以在开始时启动一个事务,并在需要时将任何插入回滚到数据库。但是当单独使用时,这个 Repository 不会导致意想不到的、不可预测的行为吗?
nhibernate - DDD、存储库和角色接口
我非常感谢人们对以下设计问题的意见。
我有一个模型,其中“个人”或“企业”可能是某个“服务”的提供者。示例类定义如下所示:
IProvider
指导ID
人员:IProvider
Guid Id
字符串FirstName
字符串LastName
业务:IProvider
Guid Id
字符串名称
服务
指南ID
IProvider提供者
因此,我在我的领域中创建了相关概念,“人”、“业务”、“IProvider”和“服务”。我正在努力的地方是创建存储库的实体。在这种情况下,“服务”是一个聚合根,因此有自己的存储库。在我的上下文中,“业务”也是一个聚合根,因为即使它不是提供者,它也会有意义。只有当他们是“提供者”时,才会在系统中创建“人员”。
我会为 IProvider 的角色创建一个存储库,它会返回“Person”和“Business”的实例吗?我的问题是代码可能很快变得非常复杂,因为任何实现都需要查看多个表等以返回所有不同类型的 IProvider。这种方法需要创建“Person”和“Business”的存储库并将其注入“IProvider”存储库以提供所需的功能,即
另一种方法是为实现“IProvider”接口的“Person”和“Business”实体创建存储库,从而使它们可以参与该角色。IE
然后,我会在需要时使用机制(即 IoC 容器)来选择 IProviderRepository 的正确具体实现。例如,如果我正在与我知道是一个人的提供者打交道,我可以获得 PersonRepository 实现。
另一种选择是不实现任何 IProvider 存储库,只使用“Person”和“Business”存储库并在服务层中根据需要使用它们?
c# - 在 RavenDb 中引用值对象父实体对象
我最近一直在玩 RavenDB,有些事情让我有点恼火。
我有一个带有一系列值对象的实体对象,例如
有很多次我想将 Bar 类型的对象传递给方法/类,但在某些时候想要引用回父实体。在 NHibernate 的世界中,如果我将其配置为 1..* 关系,那真的很容易,例如
然而,RavenDb 并不真正喜欢这导致我不得不创建如下方法:
而不是
有什么方法可以通过 RavenDb 实现这一目标?
我意识到 RavenDb(以及一般的文档数据库)促进了一种不同的处理实体的方式,如果这只是我在关系/规范化世界中花费太长时间的一个例子,任何人都可以解释我应该如何构建我的代码以更多的文档数据库方式?
domain-driven-design - DDD、BoundedContexts、领域事件和事务
我目前正在根据 DDD 的原则为产品/政党管理系统创建概念证明。其中一项要求是各方数据(客户、经销商)将存储在网络上的 CRM 中,而产品特定数据将存储在本地 SQL 数据库中(见下文)。
CRM 系统(基于 Web)
客户(包含姓名、地址、电子邮件、联系方式等)
经销商(包含姓名、地址、活动状态)
产品系统(本地)
BaseProduct(定义普通产品)
ResellerProduct(定义将由经销商销售的基本产品的定制) CustomerProduct(定义客户注册的产品)。
我一直在对如何实施解决方案的不同方法进行大量研究,但我正在努力理解所有概念以及它们如何应用于我的问题。
我首先将这些区域分成 2 个不同的有界上下文,Party 和 Product。我为这些上下文中的每一个定义了域模型,并且在其他模型中实体引用了概念时,我将它们保留为简单的 Guid Id(即产品域中的 CustomerProduct 将具有相关 Guid 的 CustomerRef 值)。
然后,我实现了一个使用对 Web CRM 的 API 调用的 Party 基础架构实现和一个使用 NHibernate 的 Product 基础架构实现。对于其中的每一个,我都实现了一个 UnitOfWork,这样我就可以控制每个上下文中的事务过程。应用层将在运行时注入两个上下文并使用它们。
例如:
- ApplicationService.RegisterNewProduct(customerId, ProductId)
- 启动每个工作单元的事务(PartyUnitOfWork.Begin()、ProductUnitOfWork.Begin())
- 使用每个上下文的存储库来查找相关的域对象(Party.Customer.Find(Id)、Product.ResellerProduct.Find(Id))
- 执行逻辑以确定客户是否符合条件、产品是否有效等。
- 创建一个新的 CustomerProduct 并保存到产品系统
- 提交每个上下文 UnitOfWork
然后,我开始进一步探索有界上下文,并考虑重构应用程序,以便每个上下文对另一个上下文的概念表示有限,但仅特定于该上下文。即在产品上下文中,我将有一个客户实体,它有一个 ID、名称和活动状态,但可能没有联系偏好等。然后我将使用 DomainEvents 来协调不同系统之间的活动,以保持他们的数据是最新的(即,如果在 CRM 系统中创建了一个新客户,一个事件将由产品系统引发和处理,以更新其客户代表)。因此上面的例子会改变,所以只使用了 Product 上下文。
为了进一步混淆问题,假设“RegisterNewProduct”调用也创建了一个新客户,我是否会在产品系统中创建客户并引发将由 Party 系统处理的“NewCustomer”事件?
我有兴趣收集人们对这些想法的评论吗?
ddd-repositories - 错误跟踪器架构
我正在创建一个带有 DDD 的 Bug Tracker 应用程序(玩具一)。我有一个带有 Add / Delete / Save 等方法的 BugRepository 和一个具有相同方法的用户存储库(实际上两者都派生自 IRepository)。现在,Bug 有一个名为“Assigned To”的属性,它是数据库中存储用户 ID 的列。
现在我需要一个名为“FindAssignee”的方法,或者更确切地说,我想知道错误分配给了谁。我把它放在哪里?
我在想我可以说 --> 在 BugRepository 添加一个名为“FindAssignee”的方法。但这会返回一个“USER”对象。那样可以么?聚合根的存储库(本例中的错误)不应该只返回它们的实体(再次错误)吗?
asp.net-mvc - 如何确定添加到存储库的聚合根的 ID?
假设我有一个通用存储库接口,如下所示:
通常,通过 IRepository.Add() 添加的项目的新 ID 将由某个后端数据库确定,但仅在提交整个事务/工作单元后才确定。所以我相当肯定 IRepository.Add() 返回添加项目的新 ID 是错误的。存储库真的不应该知道 ID 是如何创建的。这个对吗?
如果是这种情况,如何确定添加到存储库的项目的新 ID,或者我什至应该这样做?我知道像 NHibernate 这样的 ORM 能够自动用具有正确 ID 的新对象替换内存中的对象,但我正在尝试设计我的存储库而没有考虑任何特定的 ORM 实现。
例如,假设我有一个客户可以在其中下订单的网站。一位新客户选择结帐并被发送到一个表格以填写他们的详细信息。此信息用于创建存储在 CustomerRepository 中的 Customer 对象。现在需要创建他们的订单信息,但订单需要通过他们的 ID 引用客户?
domain-driven-design - 除了“真实”对象之外,DDD 存储库还可以使用摘要对象吗?
假设我正在创建一个存储库来存储数字电子书,如下面的界面所示。该存储库将存储图书的实际文本,以及标识图书的元数据(书名、作者、出版商、ISBN 等)。
在大多数情况下,我不想检索这本书的整个文本,因为那样会相当昂贵。在大多数情况下,我只关心元数据,例如我可能只是想创建一个书籍列表。那么关于 DDD 是否也允许IBookRepository
拥有返回BookSummary
对象的方法是可以接受的吗?书籍摘要对象将包括元数据,但不包括书籍的实际内容。
有UpdateBook(BookSummary book)
方法怎么办?假设我想更新Book.Rating
属性,但不需要/不想从存储库中读取本书的全部内容来执行此操作。
注意:我知道使用带有延迟加载的 ORM 也可以解决这个问题,但我想设计我的存储库而不假设将使用延迟加载
domain-driven-design - 存储库的实现是否应该像它们对应的聚合一样被隔离?
使用 DDD 时拥有存储库的好处是,它们允许人们设计域模型而不必担心对象将如何被持久化。它还允许最终产品更加灵活,因为可以轻松地换入和换出存储库的不同实现。因此,存储库的实现可以基于 SQL 数据库、REST Web 服务、XML 文件或任何其他存储和检索数据的方法。从模型的角度来看,期望只有这些魔法集合可用于存储和检索聚合根对象。
现在,如果我有两个正常的内存集合,比如 anIList<Order>
和 an IList<Customer>
,我永远不会期望修改一个集合会影响另一个集合。那么相同的逻辑是否应该适用于存储库?存储库的实际实现是否应该彼此完全隔离,即使它们实际上访问同一个数据库?
例如,可以在SQL数据库中的Customers
表和Orders
表之间建立删除时的级联关系,以便在删除客户时删除相应的订单。但是,如果稍后将SQLCustomerRepository
替换为,此功能将中断RESTCustomerRepository
。
那么我认为模型应该始终假设存储库彼此完全隔离,相应地,存储库的实际实现也应该隔离,这是否正确?
那么 ifOrders
应该在删除时删除 aCustomer
是否应该在域模型中明确定义,而不是依赖数据库?通过CustomerService.DeleteCustomer()
访问当前ICustomerRepository
和的方法说IOrderRepository
。
我想我只是很难摆脱关系世界并进入 DDD 世界。我一直想从表和 PK/FK 关系的角度来思考问题,我应该完全忽略数据库的参与。