如何构建复杂的价值对象?

时间:2017-04-14 13:16:08

标签: domain-driven-design ddd-repositories value-objects

我刚开始学习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。 但是我应该如何为帖子实现存储库? 我找到了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的正确方法是什么?

UPD: 另一方面,我必须得到所有可用的标签。我永远不必用帖子改变标签。标签的名称看起来像身份。也许我根本错了?也许标签是实体?

UPD2:

这个问题告诉我,我的设计技巧很差。 因此,我的问题有两个问题。 他们是:

  1. 在实体存储库中构建值对象的正确方法是什么。
  2. 如何在我的问题中看到价值与实体之间的区别。 毕竟它看起来很清楚。根据指定的条件,标签是值。它可以由Post的存储库构建。
  3. 但这种情况是分析不良的结果。如果我看起来更宽,我会看到标签有它自己的生命周期。虽然,在帖子的上下文中,标签是不可变的。

1 个答案:

答案 0 :(得分:2)

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

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