【问题标题】:DDD: subclasses & root entitiesDDD:子类和根实体
【发布时间】:2010-11-23 04:52:48
【问题描述】:

假设我有典型的实体 Car

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

在我的域模型中,该实体将是 Aggregate根实体

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

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

假设我有另一个实体,Company 实体。它将是另一个聚合根实体。有很多人在一家公司工作,由实体 Person 表示。人可能有汽车。但是公司的总裁通常都很有钱,而且这种人,他们有法拉利:

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

在这种情况下,我有一个实体总裁,他公司聚合,它持有对法拉利的引用,这是根实体的专业化另一个聚合。

从 DDD 的角度来看这是正确的吗? 我可以/应该将根实体本身的特化视为同一聚合的根实体吗?我的意思是,在我描述的域中,实体 Ferrari 也是 Car Aggregate 的根实体(因为 Ferrari 也是 Car)?


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

我应该如何构建放置汽车的桌子?我应该构建一个带有“CarType”列(可能的值:“Car”、“Ferrari”)和一个可为空的 Nickname 列的 Cars 表吗?

或者我应该为 Cars 建立一个表,为 Ferraris 建立一个表,后者的 PK 是 Cars 的 FK?

谢谢!

【问题讨论】:

    标签: orm domain-driven-design entity aggregate


    【解决方案1】:

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

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

    法拉利也不应该从汽车继承。这在法拉利的例子中并不明显,因为他们只生产一种类型的汽车,但考虑到公司生产多种类型的汽车,如面包车、轿车、掀背车、卡车等。您可能希望为从汽车类继承的每种类型创建类。然后……你打算创建五个继承自每种类型的 Toyota 类吗?比如……

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

    那太荒谬了。

    免责声明:我对汽车一无所知。不过……

    不要使用继承来为您的域建模。永远。

    尝试不使用继承,如何持久化您的域也将变得显而易见。

    【讨论】:

    • 谢谢,lubos。但在这种情况下,在我的“真实”域中,我“汽车”,并且有“通用”汽车和 2 种特殊汽车,“法拉利”和“保时捷”必须被跟踪下来,虽然它们本质上是“汽车”。
    • 仍然,避免继承。即使您不这样做,您也将不得不在一两年内这样做,因为您的模型将来可能会变得更加复杂,以捕捉新的需求,而继承会给您的设计带来很多痛苦。解释继承的教科书示例 Cat 和 Dog 都是 Animals 和 Circle 和 Square 都是 Shapes 给人的印象是继承到底是什么,程序员经常滥用这个强大的概念来建模域。在这里阅读更多:geocities.com/tablizer/oopbad.htm#overeng(我不是作者)
    【解决方案2】:

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

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

    现在谈谈那辆法拉利。在标准的 DDD 术语中执行这一点有点困难。通常,您会对将汽车分配给总统进行一些验证。或者也许有一个专为总统准备的 CarBuyingService,可以确保您做对了。通常在 DDD 中,特化本身并不是根聚合。

    【讨论】:

    • 你是唯一真正回答问题的人
    【解决方案3】:

    我认为,通过创建这些实体的具体类型,您开始失去系统的很多灵活性。您所暗示的关系类型是我通常使用“类型”实体处理的。例如,你有一辆车。法拉利是一种汽车。从中产生的两个实体是 Car 和 CarType。

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

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

    编辑关注

    我发现我误读了您所写的部分内容。我看到这个昵称并非适用于所有车辆,而仅适用于法拉利:汽车。我认为答案确实是“取决于”。您在模型的其余部分有多少领域专业化?拥有昵称可能在法拉利实体中很普遍,但它是独一无二的吗?它不仅与实际数据有关,而且与要求有关。基本上,这取决于您对这些实体的专业化程度。

    【讨论】:

    • 太棒了!谢谢!实际上,在我的“真实”系统中,“汽车”非常重要,但“法拉利”是我领域中最重要的东西,我不能忘记,我必须对其进行统计。
    猜你喜欢
    • 2016-11-05
    • 2017-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多