【问题标题】:Is it ok for a DDD repository work with summary objects in addtion to "real" objects除了“真实”对象之外,DDD 存储库还可以使用摘要对象吗?
【发布时间】:2011-06-09 21:40:55
【问题描述】:

假设我正在创建一个存储库来存储数字电子书,如下面的界面所示。此存储库将存储图书的实际文本,以及标识图书的元数据(书名、作者、出版商、ISBN 等)。

public interface IBookRepository
{
    void AddBook(Book newBook);
    void DeleteBook(int bookId);
    void UpdateBook(Book updatedBook);
    Book GetBook(int bookID)
} 

public class Book
{
    public int BookId {get; set;}
    public string Title {get; set;}
    public string Author {get; set;}
    public IList<Page> Contents {get; set}
}

public class Page
{
    public int PageNumber {get; set;}
    public string PageContent {get; set;}
}

在大多数情况下,我不想检索这本书的整个文本,因为那样会相当昂贵。在大多数情况下,我只关心元数据,例如我可能只是想创建一个书籍列表。那么就 DDD 而言,允许 IBookRepository 拥有返回 BookSummary 对象的方法是否可以接受?书籍摘要对象将包括元数据,但不包括书籍的实际内容。

如果有一个UpdateBook(BookSummary book) 方法呢?假设我想更新 Book.Rating 属性,但不需要/不想从存储库中读取本书的全部内容来执行此操作。

public interface IBookRepository
{
    //Full Book Methods
    void AddBook(Book newBook);
    void DeleteBook(int bookId);
    void UpdateBook(Book updatedBook);
    Book GetBook(int bookID)

    //BookSummary Methods
    BookSummary GetBookSummary(int bookID)
    IEnumerable<BookSummary> GetBooksByAuthor(string authorName);
    IEnumerable<BookSummary> GetBooksByGenre(int genreId);
    void UpdateBook(BookSummary bookSummary);
} 


public class BookSummary
{
    public int BookId {get; set;}
    public string Title {get; set;}
    public string Author {get; set;}
    public int PageCount {get; set;}
}

注意:我知道使用带有延迟加载的 ORM 也可以解决这个问题,但我想设计我的存储库而不假设将使用延迟加载

【问题讨论】:

    标签: domain-driven-design repository-pattern ddd-repositories


    【解决方案1】:

    如果您的域中有一个支持它的用例,为什么不创建具有自己的存储库的附加实体 BookSummary 来完成这项工作? BookSummary 的持久化位置并不重要——它与域无关。

    重要的是使用通用语言从域中派生实体,而不是查看数据库结构。

    public interface IBookRepository
    {
        //Full Book Methods
        void Add(Book Book);
        void Delete(Book Book);
        Book findById(int bookID)
    }
    
    public interface IBookSummaryRepository
    {
        //Full Book Summary Methods
        void Add(BookSummary BookSum);
        void Delete(BookSummary BookSum);
        Book findById(int bookSummaryID)
    }
    

    如果你的 Repository 有方法 update() 或 store() 它更可能是 DAO 而不是 DDD 的存储库:http://codebetter.com/iancooper/2011/04/12/repository-saveupdate-is-a-smell/

    【讨论】:

    • 是的,但是如果将 Book 添加到 IBookRespository,我的域确实希望找到 BookSummary。如果它们是单独的存储库,则无法确保将 Book 添加到其中会导致 BookSummary 出现在另一个存储库中。当然,您可以设置两个指向同一个数据库表的存储库,但这将是一个特定的实现。对我来说,拥有一个存储库意味着来自该存储库的所有数据都来自同一个数据存储。请参阅与此相关的我最新的 SO 问题:bit.ly/jjUhbi
    • 你可以从另一个仓库调用一个仓库。
    • 我认为仓库不应该调用其他仓库:stackoverflow.com/questions/1364461/…,
    【解决方案2】:

    在 DDD 存储库中应仅与聚合根一起使用。在你的情况下,我想,BookSummary 只是 Book 聚合中的一个实体(当然,这里需要更准确的分析),所以应该通过 BookRepository 获取 Book,然后使用延迟加载从聚合根遍历到 BookSummary。否则,您将不会在此处应用领域驱动设计

    【讨论】:

    • 我为 BookSummary 添加了代码。我不认为 BookSummary 是 Book 聚合中的实体。 BookSummary 与对应的 Book 具有相同的 ID。它以不同的形式代表相同的事物。它只是一本书的简化/代理版本。我知道使用带有延迟加载的 ORM 将消除对 BookSummary 类型的需求。然而,假设将使用延迟加载来设计存储库似乎并不比假设使用 SQL Server 来设计存储库更正确。接口应该独立于实现。
    • 我建议你没有 BookSummary。这里有几个选项: 1. 仅加载需要的数据(使用惰性属性或投影) 2. 引入分离的读取优化存储(在此处应用 CQRS 原则) 3. 将 Book 拆分为 Book 和 BookText(这里没有重复数据,就像您在当前模型中所拥有的一样)。无论如何,如果您不引入读取存储,则必须加载整个聚合(是否使用延迟加载)。如果您不这样做,您将归结为数据驱动的方法
    【解决方案3】:

    这是一个老问题,但没有公认的答案,所以我会回答,以帮助从谷歌登陆这里的人们。我一直遇到这个问题,因为我们有许多显示部分信息和/或从多个实体编译的信息的摘要屏幕。我所做的是使用@xelibrion 在他的答案的 cmets 中建议的单独读取模型。我没有使用完整的 CQRS,只是使用查询方法的简单读取模型。所以你的 BookRepository 仍然是这样的:

    public interface IBookRepository
    {
        void AddBook(Book newBook);
        void DeleteBook(int bookId);
        void UpdateBook(Book updatedBook);
        Book GetBook(int bookID)
    }
    

    您的 BookSummary 读取模型及其方法如下所示:

    public class BookSummary
    {
        public int BookId {get; set;}
        public string Title {get; set;}
        public string Author {get; set;}
        public int PageCount {get; set;}
    } 
    
    public interface IBookSummaryQueries
    {
        BookSummary GetBookSummary(int bookID)
        IEnumerable<BookSummary> GetBooksByAuthor(string authorName);
        IEnumerable<BookSummary> GetBooksByGenre(int genreId);
    }
    

    注意,这里没有更新Book的方法,只是查询

    【讨论】:

      猜你喜欢
      • 2017-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-04
      • 2013-06-13
      • 1970-01-01
      • 2021-11-08
      相关资源
      最近更新 更多