1

我正在实践领域驱动设计,那么为什么不建立一个演示产品目录项目?显然Product在这里是核心领域,但因为我喜欢让项目更有趣,我很乐意支持嵌套Category层次结构。换句话说,Category可能有许多孩子Category领域驱动设计:如何模拟嵌套产品类别层次结构?更糟糕的是,如果产品类别是一个聚合根?

此外,我想分开Category域从Product域,并使其自己的支持域。

问题:标记Category as AggregateRoot听起来不正确。 A Category可能有许多孩子Category,这也是AggregateRoots?!!我怎样才能对这个建模进行建模?嵌套产品类别在电子商务现实生活中非常普遍。


命名空间DL.Demo.Domain.Shared

public abstract class ValueObjectBase<T> : IEquatable<T> 
    where T : ValueObjectBase<T> 
{ 
    public abstract bool Equals(T other); 
    public abstract override bool Equals(object obj); 
    public abstract override int GetHashCode(); 
} 

public abstract class EntityBase : IEquatable<EntityBase> 
{ 
    public Guid Id { get; private set; } 

    protected EntityBase() 
    { 
     this.Id = Guid.NewGuid(); 
    } 

    // Some Object overrides 
} 

,我实际上是从EntityAggregateRoot inherents因为我想只有一个Entity可能是一个AggregateRoot

public abstract class AggregateRoot : EntityBase 
{ 
} 

命名空间DL.Demo.Domain.Catalog

public class Category : AggregateRoot 
{ 
    public string Name { get; private set; } 
    public Guid? ParentCategoryId { get; private set; } 
    public CategoryStatus CategoryStatus { get; private set; } 
} 

有AggregateRoot的嵌套列表只是不健全的权利我。如果您不将Category标记为AggregateRoot,那么您将如何对此进行建模?

我是DDD和所有其他相关的酷的东西,如域名事件,事件采购等新的我将不胜感激,如果有经验的人可以告诉我,如果我去正确的方式。

+1

那么,一个类别有什么行为?你需要在这些行为中执行哪些不变量?有很多方法可以模拟这个问题,这完全取决于行为和真实的不变量。 ProductCategory听起来不像一个非常有趣的聚合。我怀疑大多数行为将会是CRUD,因此域模型模式可能不是正确的选择。 – plalx

+0

+1,我不会使用DDD战术模式(聚合等)为'类别',除非他们干涉'产品'域规则中复杂的方式。 – guillaume31

+0

我正在做一些事情,因为这只是一个演示项目:当网站的管理员决定停用某个类别时,需要停用直接链接到该类别的所有产品以及该类别下的所有子类别,以及所有产品链接到这些类别?也可能需要发送电子邮件给所有其他管理员。此外,我们可能需要支持在其他类别下移动类别。这是一个复杂行为的好例子吗? –

回答

0

我是DDD和所有其他相关的酷东西,如域名事件,事件采购等新的我将不胜感激,如果有经验的人可以告诉我,如果我去正确的方式。

你是对的。

Category应该是Aggregate root,通过它的ID引用父类别,这非常好。

即使没有特殊的不变量来保护,因为这个层次结构可以在Read models中投影出不同的模式,所以嵌套类别是一个很好的事件采购候选项。尽管Aggregate非常简单,但您对该表示没有任何限制。在使用Read model你可以有不同的实现他们为每一个

  1. 与父模型树结构参考
  2. 模型树结构与子引用
  3. 模型树的结构与祖先
  4. 模型树的数组与物化路径结构
  5. 使用嵌套
  6. 模型树的结构设置

See more here about implementing tree structures(该链接指向MongoDB,但不相关)。

Category Aggregate刚刚发出简单的事件ACategoryWasCreatedACategoryHasMovedToOtherParent等和Read models刚刚适应,以反映这些事件。

我已经实现了像这样的树形结构,并且在读取端(查询端)的查询是非常快的。您可以选择一个类别中的产品和不加入任何类别的所有类别。或者,您可以再次建立一个类别路径,而无需连接。

+0

我不担心类别是聚合根。我唯一觉得直的是一个类可以有子汇总根?!我没有在我的帖子中提及域名事件,但据推测聚合根域也会包含域名事件列表。所以父类别会有自己发出的事件列表,但它的孩子也有他们自己的事件列表?!这听起来不对。 –

+1

我是否应该将CategoryTree设置为Aggregate root,而不是将类别设置为Aggregate root,并且它只是有一个Categories类别列表来启动递归类别层次结构?这样,CategoryTree可以包含整个Aggregate的所有域事件。 –

+0

@DavidLiang你只保留'Aggregate'上的parentId,而不是整个父级或树。你不需要。该树建立在“读取模型”上。每个实例仅为其自身发出事件。 –

0

定义聚合的关键是首先定义一个事务边界。除了聚合边界以外,一致性最终是通过对聚合发出的域事件做出反应来实现的。

聚合可以持有另一个聚合ID(值对象)作为参考,但是,不负责与另一个聚合在事务上一致。

所以,主要问题 - 您的树在事务上是否一致?如果是的话,链表就不会很好地扩展。你必须以不同的方式进行建模。

建模是上下文特定的,并不是饼干练习。也许你的类别只是一个可以作为路径建模的价值对象。很难说没有更广泛的背景。

0

如果你想有一个类别树,那么树本身可能应该是你的聚合根(我已经看到你已经在你自己的评论中得出了这个结论)。这将有添加或删除孩子等功能。

是的,对于非常大的树木,您可能会获得很多性能,例如以json格式(存储在MongoDb,缓存,文件或其他)中的树的只读投影。特别是考虑如何更新这样的类树通常只读取其中的一小部分,您也可以轻松地离开,只需保持json和前面的规范化数据库表树。

相关问题