DDD中的实体继承

3

我正在尝试使用DDD指南设计电子商务系统的领域模型。我有一个情况,其中有2种不同类型的产品。一种产品是“项目”,另一种产品是“包”(项目的组合/捆绑)。到目前为止,我的领域模型如下:

public abstract class Product : IAggregateRoot
{
    public string ProductId { get; protected set; }

    // Many to Many relationship between Item and Package
    protected List<ItemPackageRelationship> ItemPackages => new List<ItemPackageRelationship>();

    protected List<Image> Images => new List<Image>();

    // Other properties shared between Item and Package
}

public class Item : Product
{
    // All properties and methods specific to Item

    public void SetImages(List<string> images)
    {
       // Set images for Item has its own business logic

       // Example: number of allowed images, size of image etc
    }
}

public class Package : Product
{
   // All properties and method related to package

    public void SetImages(List<string> images)
    {
       // Set images for Package has its own business logic

       // Exampe: number of allowed images, size of image etc
    }
}

所有的行为都定义在Item和Package类中,而Product类只有共享属性。最初我考虑创建两个完全独立的聚合根(Item和Package),而没有Product实体。我需要Product仅仅是因为系统中唯一的ProductId。而所有与其他实体(如Images)的关系都需要基于ProductId。
我并不100%确定我设计的是否正确,需要一些意见。仅仅为了共享一些属性而创建基础实体是否违反了DDD的原则?还有其他什么建议可以改进我的领域模型设计吗?
非常感谢您提前的帮助。

一个 EF 类模型并不是一个领域模型。它是数据访问层的一部分,应该被设计成尽可能顺畅地执行该任务。认识到这一点应该帮助您设置正确的优先级。至于其他方面,这更多是基于个人观点的。 - Gert Arnold
1个回答

2
我认为实体的继承应该由行为而非属性驱动。 换句话说,您应该查看其他业务代码,而不是实体本身。如果您看到相对较多的地方需要以相同方式操作 ItemPackage,则可以使用继承(但不是严格必需的)。如果唯一的原因是共享唯一标识符,则继承是不合理的。

使用同一张表或不使用都是实现细节,它不应影响领域的设计!考虑逻辑和行为。如果存在许多适用于“项目”和“包裹”的通用逻辑,则可以使用继承(但是如果逻辑位于实体之外,则也可以使用接口)。另一种方法:可以先不使用继承,如果在某个时刻发现为“包裹”和“项目”编写了大量重复代码,则可以重构为继承。 - poul_ko
接口对我来说听起来是个好主意。就像我提到的,因为Item和Package都是产品,它们有一些共享属性...比如重量、价格等等。对于Item,重量是基于Item重量计算的,但对于Package,它是该包裹中所有重量的总和(这只是一个例子,Item和Package之间有6-8个共享属性,具有不同的实现)。我还尝试使用Product实体和ProductType(Item或Package)设计我的模型。但最终我得到了很多IF检查(根据类型返回值)。 - nafr1
现在,如果我使用接口,并创建2个聚合(Items和Package),那么我将拥有一些共享的值对象(ProductImage、ProductLocation等)。您认为在这种情况下在聚合之间共享值对象是否可以吗? - nafr1
我无法给出明智的答案,因为我不了解你的领域、价值对象的要求以及其周围的逻辑。但是共享价值对象本身并不是一个坏主意。我不能反对它。 - poul_ko
我得出了相同的结论。我遇到了相同的情况,我在两个实体中使用了一个常见的数据结构,但是我得到了不同的方法名称和略有不同的方法。将代码移动到公共抽象祖先中将意味着LSP违规。因此,我更喜欢使用组合,并将代码的相关部分移动到VO中。实体中的其余代码仅更改具有相同VO类型的属性,可能是10行公共代码,因此我宁愿复制该部分而不是拥有公共祖先。在某些情况下,当我们谈论DDD时,DRY原则可能会被违反。 - inf3rno
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接