我刚刚开始学习DDD。所以我为愚蠢的问题道歉...
所以我有Post
实体。看起来不错。但它应该有tags
. 在代码中,它看起来像这样(拼音代码):
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:
这个问题告诉我,我的设计能力很差。 正因为如此,我的一个问题中有两个问题。 它们是:
- 在实体存储库中构建值对象的正确方法是什么。
- 如何在我的问题中看到价值和实体之间的区别。 毕竟它看起来很清楚。根据指定的条件,标签为值。没关系,它是由Post的存储库构建的。
但这种情况是分析不良的结果。如果我能看得更宽,我会看到标签有自己的生命周期。但是,在帖子的上下文中,标签是不可变的。
标签很可能只是域中的常规值对象。标记可以是实体(如果它有自己的生命周期)。坦率地说,我认为您的域中并非如此,因为您可以将每个标签替换为具有相同属性的另一个副本。
您可以添加方法以将标签查询到域存储库。这并不违反 DDD 聚合规则。聚合实际上与一致性有关 - 如果您可以在聚合上下文之外修改部分聚合,则存储库不应返回部分聚合。但是,您可以显式返回聚合的值对象,仅用于读取目的(例如,收集所选日期范围内所有帖子的所有标签)。除此之外,为了提高效率,查询方法应该放在存储库中。话虽如此,在您的情况下,最好的解决方案可能是按照 CQRS 原则使用单独的读取模型(例如使用 nosql db)。这样,您就可以根据查询需求显式调整模型,因此它可以非常高效。