(DDD) 检索存储在多个数据库中的实体

(DDD) Retrieve entity stored in multiple databases

我正在着手开发一个新的 (dotNet Core) WebAPI,其中一些数据将从应用程序的数据库(使用 EFCore)读取,一些数据来自 Microsoft Graph。特别是,可以从 Graph 中检索到一个人的电子邮件地址和全名,并且特定于应用程序,我们想知道该人何时注册该应用程序以及该人最后一次登录的时间。在领域驱动设计方面,我会说所有这些属性都属于一个实体 User。但是,我正在为如何填充这样的用户实体而苦苦挣扎。是否有任何关于如何填充其信息存储在两个不同数据库/服务中的对象的指导?注意Graph中的信息不需要更新。

我正在考虑 3 个选项:

  1. 最简单的选择是将实体直接存储在数据库中。使用 UserRepository 我可以首先从数据库中获取记录,然后使用从 Graph 中检索到的数据对其进行补充。

  2. 更详细的选项是让 类 对应于数据库结构,并让 UserRepository 从各种数据库/服务中获取数据,然后将其映射到一个真实的实体。此选项的好处是实体与数据库完全断开连接,但是将实体来回映射到数据库记录需要做大量工作。

  3. 我想到的最后一个选项是将与应用程序相关的用户信息(存储在数据库中)和与身份相关的信息(在 Graph 中)视为两个不同的对象。合并这两个数据的负担将落在我的 API.

    的消费者身上

如果预算有限且期限紧迫,您会怎么做?

我不得不承认我并不是真正有意识的 DDD 追随者。

您可以有一个 UserAppUsageHistory 实体或类似实体,将数据放在那里(您的选项 3)。您只需要一种明智的方式让 API 消费者加入他们 (UserId?)。假设 UserAppUsageHistory 数据比 User 数据更频繁地更新,这个 可能 工作得很好。

顺便说一句,从 API 消费者的角度来看,您确实想问消费者他们想要什么(例如共同设计),因为这是获得 API 的最佳方式这对它的消费者有好处。

(your option 2) ...but there is a lot of work going into mapping entities back and forth to database records

从 API 消费者可用性的角度来看,这听起来有点令人担忧,如果这对你来说很难,对他们来说也不会更容易。

选项 1 和 3 存在延迟问题 - 确保您可以从本地副本提供数据 - 但它是最新的?也许这并不重要?对于选项 1,您只能获得非常有限的数据(这有多适合目的?)。

1 和 3 之间的一个关键区别是历史数据的结构发生变化的可能性有多大?因为每当它发生变化时,它都可能会影响 User 触及的一切。

这归结为在您更愿意遇到哪些问题以及哪些好处之间进行权衡。

选项 3 的另一个优点是,因为它们是松散耦合的,所以您始终可以在您认为合理的任何限制内,将数据的收集工作外包给一些后台进程,以保持已知用户数据的更新。不过,对于新添加的用户,您可能需要一些东西 special/additional。

What would you have done given a limited budget and tight deadlines?

视情况而定。产生技术债务很糟糕,但有时是不可避免的。如果你能应付额外的工作,选项 3 听起来更安全、更灵活。

重要的注意事项是:

  • 数据的性质structure/schema(多少,多复杂)?越复杂 -> 使用单独的实体。
  • structure/schema 改变的可能性?如果可能 -> 远离 User.
  • 数据的易变性(记录更新频率)?
  • 您拥有的数据是最新的有多重要?

关于最后两个:设计一种策略来处理获取数据,看看这是否会影响您对问题的决定。如果不稳定,选项 3 对我来说听起来更好。

最后,你考虑过LazyLoad(模式)吗?它不能直接解决您的问题,但是如果您可以单独获取历史数据,那么它会使初始 User 实例化更简单、更快捷,因为您只需在运行时加载核心 User 数据,并且只加载在历史数据中 if/when 明确要求。

我建议的一般准则是,持久性技术(或两者的组合)是隐藏在存储库后面的实现细节。

根据 DDD,UserRepository 应该 return 一个域对象,在本例中是一个 User 聚合。这排除了 选项 3

关于选项 1 和 2,如果我理解正确,您正在考虑存储库是否应该实现映射或直接在数据库中存储实体。

我会说理想情况下你想要映射。这样,您就可以在域对象和持久性结构之间完全隔离。这应该为您的代码库提供更大的灵活性。

但是,如果您的预算有限且时间紧迫,那么不进行映射应该没问题。但是,您应该尝试确保将来可以在存储库中添加映射,而不影响存储库 API(=不影响使用存储库的任何代码)。