将DDD域模型映射到EF POCO

时间:2016-10-21 20:28:13

标签: entity-framework domain-driven-design

如果有两个客户域模型代表不同的有界上下文(具有不同/重叠字段),那么您应该如何在数据库中更新此特定X有界上下文Customer,该客户域可能在一个POCO中同时拥有这两个客户域(或者也许Y有界上下文客户另外使用这个相同上下文的订单列表?

我也可以这样说。当域模型使用数据库POCO将多个映射到一个时,如何解决案例?

这是否意味着存储库必须再次查询db,但这次是来自DB的整个POCO对象,相应地更新其值并最终进行更新?

2 个答案:

答案 0 :(得分:1)

如果您提供了Customer的2个上下文和重叠属性,那将会有所帮助。出于这个答案的目的,我会使用上下文:' Sales'和'营销'共享属性是'首选名称'

我最初的想法是基于短语'重叠字段'是你需要重新访问你的模型,因为你不应该有2个模型负责一个特定的值,否则你有并发/竞争条件。

尝试并思考您的客户如何解决过去笔下的情况?纸。谁会拥有这样的客户'文件?销售和市场营销是否都有自己的版本,或者营销是否依赖于销售副本(反之亦然)?

此外,DDD最强大的一个方面是它会强制您的持久性问题进入您所属的基础架构层。您不必为所有存储库调用使用EF,如果更容易为特定的持久性调用手工制作一些sql,那么就这样做。

- 场景1:重叠字段不重叠 -

在这种情况下,域专家意识到Sales.Customer.PreferredName和Marketing.Customer.PreferredName是独立的属性,并且在上下文之间可以不同。营销经常使用该领域为他们可爱的我们是你最好的朋友活动对应,而销售人员倾向于保持最不明确的文件

CUSTOMER数据库表有2个字段:PreferredNameSale和PreferredNameMarketing。

2个具体的存储库最终会看起来像:

class Sales.Repositories.ClientRepository : Domain.Sales.IClientRepository {
    Update(Domain.Sales.Client salesClient) {
        using (var db = new MyEfContext()) {
            var dbClient = db.Client.Fetch(salesClient.Id);
            dbClient.PreferredNameSales = salesClient.PreferredName;
            db.SaveChanges();
        }
    }
}

class Marketing.Repositories.ClientRepository : Domain.Marketing.IClientRepository {
    Update(Domain.Marketing.Client marketingClient) {
        using (var db = new MyEfContext()) {
            var dbClient = db.Client.Fetch(marketingClient.Id);
            dbClient.PreferredNameMarketing = marketingClient.PreferredName;
            db.SaveChanges();
        }
    }
}

实体框架注意到只更改了1个字段并将相应的update client set field=newvalue where id=1发送到数据库。

当销售和营销同时更新其单个客户首选名称的版本时,不应存在并发问题。

另请注意,EF提供了大量开销,而且价值很小。可以使用简单的参数化SqlCommand.Execute()

完成相同的工作

- 场景2:重叠字段重叠 -

您的模型已损坏,但要及时修复已为时已晚。你骗自己说,销售和营销试图同时改变首选名称的可能性很小,即使它发生了,也很少有人希望用户不会因为没有正确使用系统而责备自己。

在这种情况下,只有一个数据库字段:client.PreferredName和方案1一样,函数在同一个表/字段上工作:

class Sales.Repositories.ClientRepository : Domain.Sales.IClientRepository {
    Update(Domain.Sales.Client salesClient) {
        using (var db = new MyEfContext()) {
            var dbClient = db.Client.Fetch(salesClient.Id);
            dbClient.PreferredName = salesClient.PreferredName;
            db.SaveChanges();
        }
    }
}

class Marketing.Repositories.ClientRepository : Domain.Marketing.IClientRepository {
    Update(Domain.Marketing.Client marketingClient) {
        using (var db = new MyEfContext()) {
            var dbClient = db.Client.Fetch(marketingClient.Id);
            dbClient.PreferredName = marketingClient.PreferredName;
            db.SaveChanges();
        }
    }
}

显而易见的问题是,销售和市场营销同时进行的保存将最终以持久数据的方式获胜。您可以使用lastupdated时间戳等尝试缓解此问题,但它会变得更加混乱和破碎。检查您的模型并记住:DB MODEL!= DOMAIN MODEL!= UI视图模型

答案 1 :(得分:0)

  • 每个有界上下文都需要拥有自己的数据库。就是这样,这里不应该讨论。违反此规则会导致严重后果,已经多次讨论过。
  • 重叠字段的气味,不同的有界上下文有不同的关注点,因此不需要分享太多数据。最好的情况是,您唯一分享的是聚合身份。如果您的世界中有一个客户具有由两个不同的有界上下文处理的不同关注点,则可以对两个有界上下文使用一个 CustomerId 值。

  • 如果你真的需要同步一些数据,你需要在两个模型中都有它,因此在两个持久存储中(我故意避免使用数据库这个词)并且你可以同步使用域事件的数据。这很常见。