如何构建复杂的值对象?

How to build complex value-object?

我刚刚开始学习DDD。所以我为愚蠢的问题道歉...

所以我有了 Post 实体。看起来不错。但它应该有 tags。 在代码中它看起来像这样(ruby 代码):

class Post
  attr_reader :tags
  attr_reader :title
  attr_reader :text
  # ...
end

class Tag
  attr_reader :name
  attr_reader :description
  # ...
end

标签作为实体没有意义。我不需要 tag 本身。 但是我应该如何为 post 实现存储库? 我发现了 2 个变体:

1. 在同一个存储库中构建标签。像这样:

# PostRepository
def find(id)
  # getting post data from storage here
  # getting tags data
  Post.new(title, text, tags_data.map { |tag_data| Tag.new(tag_data[:name], tag_data[:description]))
end

但是看起来很难看。说不清为什么。

2. 为标签创建单独的存储库。

# PostRepository
def find(id)
  # getting post data from storage here
  Post.new(title, text, tag_repository.find(tag_ids)) # or tag_names or tag_something
end

看起来好多了。但是为值对象创建单独的存储库可以吗?

根据DDD,正确的方法是什么?

更新: 另一方面,我必须获得所有可用的标签。而且我永远不必用 posts 更改标签。标签的名字看起来像身份。也许我从根本上是错误的?也许标签是实体?

UPD2:

这个问题说明我的设计功底很差。 因此,我的问题中有两个问题。 他们是:

  1. 在实体存储库中构建值对象的正确方法是什么。
  2. 如何在我的问题中看出值和实体之间的区别。 毕竟看起来很清楚。根据指定的条件,标签是值。没关系,它是由 Post 的存储库构建的。

但这种情况是分析不当造成的。如果我能看得更远一点,我会看到标签有它自己的生命周期。但是,在 post 的上下文中,标签是不可变的。

标签很可能只是您域中的常规值对象。标签可以是一个实体,如果它有自己的生命周期的话。坦率地说,我认为您的域不是这种情况,因为您可以将每个标签替换为具有相同属性的另一个副本。

您可以将查询标签的方法添加到您的域存储库中。这不违反 DDD 聚合规则。聚合实际上是关于一致性的——如果您可以在聚合上下文之外修改它们,那么您的存储库不应该是聚合的 return 部分。但是,您可以明确 return 仅出于阅读目的对聚合对象赋值(例如,收集选定日期范围内所有帖子的所有标签)。除此之外,为了提高效率,查询方法应该放在存储库中。话虽如此,在您的情况下,最好的解决方案可能是遵循 CQRS 原则使用单独的读取模型(使用例如 nosql db)。通过这种方式,您可以根据查询需求明确调整模型,因此它可以非常高效。