DDD:子类&根实体

时间:2009-08-26 22:42:09

标签: orm domain-driven-design entity aggregate

假设我有典型的实体 Car

class Car : Entity
{
    public double MaxSpeed { get; set; }
    public Color Color { get; set; }
    /* ... */
}

在我的域模型中,此实体将是聚合根实体

现在让我说我专攻汽车。我创造了一辆法拉利,而法拉利的快乐车主喜欢用昵称来称呼他们:

class Ferrari : Car
{
    public string Nickname { get; set; }
}

假设我有另一个实体, 公司 实体。它将是另一个聚合根实体。有很多人在公司工作,由实体 Person 代表。人可能有车。但是公司的 总统 通常都非常富有,而且这类人,他们有法拉利:

class President : Person
{
    public Ferrari Ferrari { get; set; }
}

在这种情况下,我有实体总裁,内部 公司聚合,其中包含对法拉利的引用,法拉利是一个专业化的根实体另一个聚合。

鉴于DDD,这是正确的吗?我是否应该将root实体本身的特化视为同一聚合的根实体?我的意思是,在我描述的领域,法拉利实体也是汽车综合体的根实体(因为法拉利也是一辆汽车)?


现在假设我必须将此模型持久保存到数据库。我认为我的问题不依赖于我将使用的OR / M框架。

我应该如何构建持有汽车的表?我应该构建一个单独的表汽车,带有“CarType”列(可能的值:“Car”,“Ferrari”)和可以为空的Nickname列吗?

或者我应该为汽车和法拉利的桌子建造一个桌子,后者的PK是汽车的FK?

谢谢!

3 个答案:

答案 0 :(得分:4)

您不应该使用继承来为您的域建模,因为一旦模型开始变得复杂,您很快就会遇到麻烦。

总统只是个人的角色,人可以扮演多重角色。也许总统只有一个角色,但选择错误的例子只是偶然的。

法拉利不应该继承汽车。在法拉利的例子中并不明显,因为他们只做一种类型的汽车,但考虑公司制造许多类型,如货车,轿车,掀背车,卡车等。您可能希望为将从汽车类继承的每种类型创建类。然后是什么......你打算制作五种丰田类,从每种类型继承?比如...

Car -> Sedan -> ToyotaSedan
Car -> Truck -> ToyotaTruck
Car -> Hatchback -> ToyotaHatchback

那太荒谬了。

免责声明:我对汽车一无所知。然而...

不要使用继承来为您的域建模。如初。

在没有继承的情况下尝试它,并且如何持久保存您的域也将变得很明显。

答案 1 :(得分:3)

通常,当您跨越根聚合边界时,您只允许引用为其他根聚合的ID。然后,您可以使用该ID在其存储库中查找其他聚合。

因此,在您的情况下,您希望总统拥有车牌,如果您需要对总统的车做某事,您可以使用车牌ID前往存储库获取车辆。总统不会提到汽车本身。

现在关于那辆法拉利。在标准DDD术语中执行它有点困难。通常情况下,你会对总统的汽车分配进行一些验证。或者也许只为总统提供CarBuyingService,以确保您做对。通常在DDD中,特化本身不是根聚合。

答案 2 :(得分:2)

我认为你通过创建这些实体的具体类型开始失去系统的很多灵活性。你暗示的关系类型是我通常用“类型”实体交给的。例如,你有一辆车。法拉利是一种汽车。由此承担的两个实体是Car和CarType。

您正在谈论这样做的方式,每次引入新类型时都必须添加新实体。如果您想要捕获的所有内容都是汽车的“昵称”,我认为这只是另一个数据,而不是另一个实体。除非您在不同类型的Car实体中有不同的数据(即不同的属性名称)和/或行为差异,否则您不会从这种方法中获得太多收益。我宁愿拥有像FindCarByType()这样的存储库方法并处理一种类型的实体,以降低风险。

我绝不是DDD专家,我正在努力解决一些概念(或者更像是对一些概念的多种解释而苦苦挣扎)。我发现没有100%纯粹的实现,并且我看到的每个实现都有细微差别。

编辑关注

我看到我误读了你写的部分内容。我看到这个昵称并不适用于所有车辆,只适用于法拉利:汽车。我认为答案实际上是“它取决于”。您在模型的其余部分中拥有多少域专业化?拥有昵称可能在法拉利实体中很普遍,但它是否是独家的?它不仅涉及实际数据,还涉及要求。基本上,它取决于您在这些实体中期望的专业化程度。