Domain driven design 通过复合标识到聚合根目录内实体的DDD导航

Domain driven design 通过复合标识到聚合根目录内实体的DDD导航,domain-driven-design,entities,software-design,aggregateroot,Domain Driven Design,Entities,Software Design,Aggregateroot,我有一个聚合根Products,其中包含一个实体列表选择,而该列表又包含一个名为功能的实体列表 聚合根Product的标识仅为name 实体选择具有名称标识(及其相应的产品标识) 实体功能具有名称标识(以及相应的选择标识) 其中实体的标识构建如下: var productId = new ProductId("dedisvr"); var selectionId = new SelectionId("os",productId); var featureId = new FeatureId(

我有一个聚合根
Products
,其中包含一个实体列表
选择
,而该列表又包含一个名为
功能
的实体列表

  • 聚合根
    Product
    的标识仅为name
  • 实体
    选择
    具有名称标识(及其相应的产品标识)
  • 实体
    功能
    具有名称标识(以及相应的选择标识)
其中实体的标识构建如下:

var productId = new ProductId("dedisvr");
var selectionId = new SelectionId("os",productId);
var featureId = new FeatureId("windowsstd",selectionId);
请注意,从属标识将父标识作为组合的一部分

其想法是,这将形成一个产品零件号,可以通过选择中的特定功能来识别,即上述featureId对象的
ToString()
将返回
dedisvr os windowsstd

所有内容都存在于产品聚合中,其中业务逻辑用于对选择和功能之间的关系实施不变量。在我的领域中,没有选择的功能和没有关联产品的选择是没有意义的

在查询产品相关功能时,会返回功能对象,但C#
internal
关键字用于隐藏任何可能改变实体的方法,从而确保实体对调用的应用程序服务不可变(在与域代码不同的程序集中)

上述两个断言由两个函数提供:

class Product
{
    /* snip a load of other code */

    public void AddFeature(FeatureIdentity identity, string description, string specification, Prices prices)
    {
       // snip...
    }

    public IEnumerable<Feature> GetFeaturesMemberOf(SelectionIdentity identity);
    {
       // snip...
    }
}

与父母身份形成的复合身份-好做法还是坏做法?

IMHO没有理由相信这是一种不好的做法,只要实体id在聚合根中是唯一的,那么实体id是复合的,或者在聚合根之外是唯一的,就没有区别。唯一的反对意见可能是,这些复合标识符不同于您的领域词汇表中使用的标识符 语言'

对内部实体的引用-暂时的还是长期的?


如果这些实体是不可变的,则应将这些实体建模为值对象。否则,通过直接引用这些实体,您将面临访问不再与给定聚合根关联或同时已更改的实体的风险

与这个问题不完全相关,但我想首先提到,我觉得界面不吸引人。似乎您正在单向公开
特性
类。要么暴露,要么不暴露我不是C#开发人员,因此请不要介意我犯任何语法错误。为了证明我的意思:

这将公开功能的属性。当这些属性改变时,无论在哪个庄园,这个接口也需要改变

public void AddFeature(FeatureIdentity identity, string description,
                       string specification, Prices prices)

您可能想考虑接受<代码>特性< /Cord>对象作为参数:

public void AddFeature(Feature feature)
这是我的方式

关于主题;你的问题让我想起了NoSQL设计。我有点熟悉这些,所以我可能有偏见,可能没有抓住重点

将子标识符与父标识符组合在一起有多种方式,可能是一种坏做法,也可能不是。考虑如何访问您的实体。如果仅从父实体访问子实体,则聚合是有意义的。如果子实体也可能是根实体,则需要引用它们。你已经在你的问题中做出了这个决定

没有选择的功能和没有关联产品的选择是没有意义的

您的
产品
类具有某种类型的集合,其中包含
选择
对象,这是有道理的。
选择
类将有一个包含
功能
对象的集合。请注意,这可能会使
产品
对象在持久性方面非常沉重,如果它有许多
选择
对象,而这些对象可能有许多
功能
对象。在这种情况下,通过标识符将它们作为引用可能会更好

代码中使用的标识符(持久性层除外)不必同时由子标识符和父标识符组成,因为它们已经在特定上下文中。然而,这可能是一个提高数据可读性的设计决策

组合标识源于SQL,我想,我见过使用类对这些标识建模的代码。我认为这更像是持久性框架或语言的局限性。这只是为了证明手段的正当性。当持久性框架在某种程度上迫使您这样做时,它是可以接受的

引用内部实体听起来像是不应该做的事情。在
产品
的示例中,
选择
功能
;没有产品的选择是没有意义的。因此,提及该产品更有意义。对于报告,您可能需要考虑复制数据。这实际上是NoSQL中的一种常见技术。特别是当实体是不可变的时,你要考虑在别处复制这些实体。引用一个实体将导致另一个“获取实体操作”,而数据永远不会更改,如果我这么说的话,这是毫无意义的

提到父母或孩子一点也不坏。这些关系是强制的,这是建模的一部分,并不是说实体存在时没有父实体。如果要强制子实体具有父实体;在子构造函数中需要父级。请不要在父级中实现create-child方法。如上所述,这将使事情复杂化。我个人不会强迫你有一个父母,当你创造一个孩子的时候,你自己设置了父母

public void AddFeature(Feature feature)