【问题标题】:DDD - How to manage entities across bounded contexts in EF CoreDDD - 如何在 EF Core 中跨有界上下文管理实体
【发布时间】:2020-06-14 05:44:49
【问题描述】:

我还没有找到具体的例子。我知道每个有界上下文都有自己的实体版本,您不应该跨上下文共享实体。但是对于使用像 EF 这样的 ORM,我该如何管理呢?

例如,下面是我的实体和它们存在的限界上下文:

成分(实体有界上下文 A)

配方(实体有界上下文 b)
成分(实体有界上下文 b)
MenuItem(聚合有界上下文 b)

现在每个有界上下文都有自己的成分版本。但是由于我在 EF 中有一个单独的数据库上下文来管理它,我该如何安排呢?我正在使用 CQRS,因此我可以在需要时触发事件。我的计划是在我的 Recipe 实体中维护一个 id 列表,并从数据库中提取相关的成分,这样数据就不会重复。

但我不确定我的数据重复问题是否有效。在我上面的例子中,假设一家公司销售原料,但也有预设食谱(带有一份原料清单),可以在食品摊上出售。

在一个上下文中,成分与另一个实体没有关系,而在另一个上下文中,它是一个子实体(在聚合中)。我可以看到它应该如何设计(有界上下文中的单独实体)但是当涉及到数据库时,这实际上是如何设置的?如果需要在上下文 A 和上下文 B 中跟踪成分的属性/领域知识不同怎么办?这最终会成为一个单独的表吗?我对此有点迷茫。

编辑: 请记住,我在这里只使用 1 个数据库。我知道通常每个有界上下文都有单独的数据库以避免这种情况,但想知道如何通过 1 个数据库来实现。

【问题讨论】:

    标签: c# entity-framework domain-driven-design cqrs


    【解决方案1】:

    我会将您的问题分为两部分:

    1. 架构部分:有界上下文设计/边界
    2. 技术部分:数据库模式管理、EF 映射等

    从第 1 点开始,我会强调有界上下文的边界比您在问题中暗示的要厚。如果边界定义明确,则 BC 之间几乎没有数据重复,部分来自某些 Id,仅此而已。在您的情况下,BC-A 中的成分与 BC-B 中的成分具有相同的 ID。

    例如,您的 BC-A 可能会从成分中管理名称、描述、图片等。 BC-B 不需要此信息,但它可能具有创建配方所需的一些重要属性。以类似的方式,BC-C 可能有每种成分的供应商、价格等。

    此外,在您的 BC-B 中,您可能在两个地方拥有配料:一张包含所有配料及其有趣属性的表格,以及作为食谱一部分的实体。例如,该实体不会具有所有这些属性,而是具有成分 ID 加上该配方中使用的数量。

    使用这种类型的设置,不仅在原则上是不可取的,甚至在实践中,跨限界上下文共享成分表也是不可取的。

    很多时候,数据复制的需求来自 UI 需求。例如,您需要在显示食谱时显示成分的名称,但这可以通过 UI 组合来解决。 UI 可以从 BC-B 中提取食谱,并使用 BC-A 中的名称以及可能使用 BC-C 中的价格来丰富数据。

    如果您遵循这种方法,那么第 2 点应该是独立的:每个 BC 都应该有自己的 DbContext。可以为所有 DbContext 使用相同的连接字符串(数据库),但您可以使用特定的 DB 模式初始化 DbContext。这样,您可以在每个 BC 中拥有一个成分表,而不会发生 DB 表冲突,并且您也可以为每个 DbContext 拥有 EF 迁移版本表(尽管我相信它也适用于共享表)。此设置还为您提供了以下优势,即允许您在每个有界上下文的单个 DB 中分离数据库,而无需更改任何代码。出于可扩展性的原因,您最终可能需要它,或者促进基础设施即代码等。

    【讨论】:

    • 感谢您的回答。我相信这是我将要采取的方法。每个 BC 单独的 DbContext。我的一个问题是,如果为 2 个不同的 BC 共享一张桌子是个坏主意。在这种特殊情况下,我将是唯一的开发人员。我知道,由于 DDD 试图解决的问题,数据重复在一般情况下并不坏。如果我只需要访问 BC B 中的数量/名称/ID,我是否可以最终共享同一张表,而不是 BC A 中的成分表 A 和 BC B 中的成分表 B?还是在这种情况下将表格完全分开是一个更好的主意?
    • 评论太长了,但我相信一个警告是只有 BC 应该负责创建/更新,而所有其他 BC 具有只读访问权限(即在 2 个或多个 BC 共享表的情况下) - 我明白这通常不被推荐,但在合适的情况下 - 这是一种有效的方法吗?
    • 我不是说你不应该这样做,但绝对不建议这样做。此外,如果在两个不同的 BC 中需要数量和名称,我会仔细检查我的界限。对我来说,这闻起来像是不好的界限,但是如果不了解您的域的更多信息,就无法分辨。只有一个 BC 负责创建成分,这听起来是正确的。然后它可以发布一个成分创建事件,然后其他 BC 可以在他们的成分表中插入一条记录。我想所有的 BC 都可以更新他们的成分表。 BC-A 可以更新成分名称,BC 价格等
    • 在这个特定的例子中,假设 BC A 是一家销售散装原料的企业,而 BC B 是一家销售使用手头现有原料的预制食谱的子企业。 BC B 将需要 A 的成分列表(尽管他们不需要所有属性,只需要价格 + 名称)。在这个特定的例子中,我想知道我是否将所有成分的创建都留给 BC A - 而 BC B 只从 A 中提取列表并存储其“自己的”版本,即价格/名称,但使用与 BC A 相同的表。或者我可以在 BC B (recipe_ingredients) 中存储我需要的属性的表。
    • 好的。有多种方法可以实现这一点,这实际上取决于您的要求。我当然不会分享这张桌子。我也不认为名称需要在两个 BC 中,因为它不是您需要执行任何业务逻辑的属性。请注意,很多时候,我们认为我们需要共享大量数据,但实际上它归结为 UI 需求。在一个屏幕中,您可以显示来自多个 BC 的数据。在 UI 上显示食谱时,您可以从 BC-B 获取带有成分 ID 和数量的食谱,以及从 BC-A 获取名称和价格的食谱。并且如果批量价格与价格不同
    【解决方案2】:

    TL/DR:拥有单个数据库服务是完全可以的(对于较小的服务或由于某些业务特定原因),只要不共享数据。如果您使用普通形式的数据库,请使用不同的表来存储您的两种成分。

    加长版:

    如果您在这两个有界上下文中的 ingredient 实体由同一个物理支持,而且它们可以共享数据(一个成分实体可以对另一个成分实体创建的数据进行操作) ,那么它们并不是真正独立的。

    以 BC 为边界的一个非常重要的方面是完全允许这些实体独立发展。通过让他们共享一个表格(或文档集合),您不允许这种情况发生。此外,这违反了更通用的(我的意思是非 DDD 特定的)准则“您不应该跨服务边界共享数据库”。

    【讨论】:

    • 我想我也得出了这个结论,昨天我也尝试对这个特定问题进行大量挖掘。我最终将使用相同的数据库,但可能会使用不同的模式来分离围绕 BC 的关注点。
    • @Help123 不同的架构可能还不够;它应该是不同的表。
    • 以上我只是想说明,如果使用的schema在某些属性上有所不同,但存储成分的表仍然相同,这是不够的。
    猜你喜欢
    • 1970-01-01
    • 2014-08-22
    • 1970-01-01
    • 1970-01-01
    • 2011-10-03
    • 2021-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多