繁体   English   中英

DDD 通过间接父实体 ID 访问实体

[英]DDD Accessing an Entity by an indirect parent Entity Id

我正在构建一个集成Plaid API的应用程序以访问用户银行信息(登录、帐户、交易等)。 我正在尝试遵循 DDD 原则。

以下是 Plaid API 流程如何工作的一般概念:

  1. 用户为某个银行机构提供他的电子邮件/密码。 如果有效,则创建一个格子项目 这个 object 将用户与一组银行凭证相关联,并包含一个访问令牌,可用于与 API 进一步交互。
  2. 每个格子物品都可以访问一组特定的银行帐户
  3. 每个银行账户都有一组交易

到目前为止,我在我的域层中创建了 3 个实体:项目、帐户和交易。 我为每个创建了一个包含基本 CRUD 操作的存储库。

public class Item
{
    public string Id { get; set; }
    public string AccessToken { get; set; }
    public string UserId { get; set; }
    ...
}

public class Account
{
    public string Id { get; set; }
    public string ItemId { get; set; 
    ...
}
public class Transaction
{
    public string Id { get; set; }
    public string AccountId { get; set; 
    ...
}

可以看到,这些实体之间的关系是:

用户拥有物品-> 物品拥有账户-> 账户拥有交易

我的问题是,当我需要通过间接父项查找实体时会发生什么? 例如: GetTransactionsByItemIdGetAccountsByUserId 基于DDD,这个逻辑go应该放在哪里?

由于我的数据结构(1-多关系的非 SQL 链),我知道我必须分多个步骤进行此类查询。 但是,我读到 Repository 应该只关心它自己的实体,所以我怀疑将 ItemsRepository 和 AccountsRepository 注入 TransactionsRepository 以添加GetTransactionsByItemId方法可能不是一个好主意。

我还阅读了有关将许多存储库注入服务并从内部管理所有这些“连接”的信息。 但是,我无法为该服务想出一个名称,所以我担心那是因为从概念上讲这没有多大意义。

我还阅读了有关聚合的信息,但我不确定我是否识别出这些实体中的根。

我能想到的另一种选择是尝试通过向每个事务添加 ItemId 来缩短关系。 但是,由于我是如何从 api 获取数据的,所以这需要破解。

我会说你的聚合根将是一个项目。 如果我的结构正确,则没有帐户就不能存在没有项目和交易的帐户。 所以你可以只使用 ItemsRepository:

public class ItemsRepository
{
  public async Task<Item> GetById(long id, IncludesSpec includes)
  {
    return await this.context.Items
      .Where(c => c.Id == id)
      .Include(c => c.Accounts).ThenInclude(c => c.Transactions)
      .SingleOrDefaultAsync();
  }
}

比你得到一个包含所有加载数据的项目。 IncludesSpec 由您决定:它将包含应该制作的包含内容,以及应在存储库方法中动态添加的包含内容。

从 .net ef core 5 开始,您可以执行过滤包含,如.Include(c => c.Accounts.Where(...)),因此您可以根据您的要求进一步缩小实际包含范围。 您可以传递另一个包含此过滤器信息的参数。

此外,您的项目应将 Accounts 公开为只读集合(使用 EF 的支持字段)并提供方法 AddAccount() 以便没有人可以将您的 DDD 项目修改为纯实体。

我认为,最有意义的是拥有一个注入多个存储库的服务。

您可以有一个返回Item对象的ItemRepository ,一个返回AccountAccountRepository ,一个返回TransactionRepositoryTransaction和一个返回UserUserRepository

如果您的数据 model 使得在一个请求中执行查询变得很麻烦,那么在您的服务中有一个 function 是事务性的(ACID:要么全部完成要么全部回滚)它对每个注入的存储库执行不同的查询,然后构建对象并返回它们。

如果您确实看到了一种使其成为一个查询的方法,则可以在相关存储库中对该查询进行硬编码。 来自领域驱动设计,埃文斯:

硬编码查询可以构建在任何基础设施之上,无需大量投资,因为它们所做的正是某些客户无论如何都必须做的事情。

在有大量查询的项目上,可以构建一个 REPOSITORY 框架,允许更灵活的查询。[...]

通过框架概括 REPOSITORIES 的一种特别合适的方法是使用基于 SPECIFICATION 的查询。 SPECIFICATION 允许客户描述(即指定)想要什么,而不用关心如何获得它。 在此过程中,创建了一个可以实际执行选择的 object。 [...]

即使是具有灵活查询的 REPOSITORY 设计也应该允许添加专门的硬编码查询。 它们可能是封装常用查询或不返回对象本身的查询的便捷方法,例如所选对象的数学摘要。 不允许此类意外事件的框架往往会扭曲域设计或被开发人员绕过。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM