用于在视图中显示数据的DDD和优化

时间:2011-12-05 10:00:51

标签: language-agnostic domain-driven-design

这是许多基于Web的项目中的常见要求:实体必须向另一个相关实体显示信息。例如,电子商务网站中的书籍必须显示有关其作者的相关信息。

假设我将图书和作者都作为一个实体进行建模,我应该如何实现在同一页面上显示图书及其作者信息的功能。

  • 我可以调用BookRepo来检索书籍的信息,然后再调用AuthorRepo来检索作者,使用书籍实体中的authorid。这是2个查询
  • 我可以编写一个查询,我将Book和Author表连接在一起,并在1个查询中检索这两个信息。但是这个查询的哪个回购呢?这会破坏DDD,因为我假设有关Book and Author实体的详细信息吗?

哪种'最佳做法',以及我可以通过其他方式解决这个问题?

(我假设使用标准SQL查询[例如PHP + MySQL],因为在EF 4中你可以定义Book和Author之间的关联,这可以很容易地解决问题)。

3 个答案:

答案 0 :(得分:1)

但是即使您似乎没有使用ORM,您可能需要从SQL查询中填充实体,并且您的实体必须以某种方式相互关联(集合,导航属性等...)。 如果您的域包含彼此之间没有关联的实体,我不会将其称为DDD,因为您缺少一些重要的成分,如聚合,值对象,双向/单向关系。 但我知道什么:-)也许你已经把你的谜题弄好了,最后一件事就是将实体合并到一个对你的客户有用的“视图”中。

由于存储库通常在聚合根实体上运行,因此您可以使用ListBooksByAuthor(BookRepository)或ListAuthors(AuthorRepository)等存储库方法。

如果要在网页中显示来自多个不同聚合的复杂数据,我建议使用数据传输对象。让DTO对象对于该页面或用例是唯一的,并且是显示网页所需的所有(或大部分)数据的“视图”。 我还建议不要在任何地方使用DTO,除非您使用的是Web服务。使用DTO一起提供优点和缺点。它与服务层一起为您提供了一个反腐败层,并为您提供了注册Book和Author存储库的位置。然后从服务层你可以组装和重新组装DTO(看看AutoMApper或类似的...帮助你很多)。 但是到处都是DTO,也会给你带来维护应用程序的开销。它增加了另一层维护。 我更喜欢将它用于某些客户/网页。

我希望你理解我想要解释的内容: - )

答案 1 :(得分:1)

如果你看一下this page,它描述了两种加载和关联聚合根的方法。将此链接回您的示例:

Book类将相关的作者信息封装为值类型,因此当Web信息显示在Web页面上时,它具有所需作者的所有信息。如果用户决定查看有关作者的更多信息,他们可以通过链接到作者页面(无论要求是什么)。

如果您有一个名为FindBookByTitle的服务方法,那么 然后,加载Book实体将从BookRepo加载相关的作者信息。

class Author
{
  public Author(int authorID, FullName name) { }

  int AuthorID { get; }
  FullName Name { get; }
  List<BookDetails> AuthoredBooks { get; set; }
}

class BookDetails
{
  public BookDetails(int ID, string title) { }

  int BookID { get; }
  string Title { get; }
}

class Book
{
  public Book(int ID, string Title) { }

  int BookID { get; }
  string Title { get; }
  List<AuthorDetails> Writers { get; set; }
}

class AuthorDetails
{
  public AuthorDetails(int ID, FullName name) { }

  int AuthorID { get; }
  public FullName fullName { get; }
}

class FullName
{
  public FullName(string name, string surname) { }

  public string Name { get; }
  public string Surname { get; }
}

答案 2 :(得分:1)

没有银弹解决方案,但你有几个选择。

您首次提出的调用两个存储库的解决方案是完全有效的,并且在实践中始终发生。例如,它需要超过一百种不同的服务才能呈现Amazon产品页面。每项服务都负责提供特定于其有界上下文的数据。您可以创建名为BookService的服务,该服务调用两个存储库以返回报告对象,或者具有特定视图所需的所有数据的DTO。如果您认为性能是由两个存储库调用引起的,那么您可以使用缓存或CQRS来创建适当的读取模型,但不要过早地跳转到这些解决方案。

但是这个查询的回复是什么? 我只是将它添加到BookRepository,甚至是一个名为BookDetailsReportingRepository的全新存储库,也许是一个名为GetBookDetails的方法。此方法不会返回可编辑实体,而是返回报告对象,该对象是来自多个实体的值的投影。

这会破坏DDD,因为我假设有关Book and Author实体的详细信息吗? 这不违反DDD,我认为更容易申请。只需将上述存储库返回的数据视为报告对象,而不是实体。