【问题标题】:Repository Interface(s) in Domain-Driven Design领域驱动设计中的存储库接口
【发布时间】:2011-05-31 05:01:33
【问题描述】:

关于域和存储库之间的合同,我认为最好避免使用 Create() 和 Delete() 等方法的包罗万象的通用 IRepository 接口?当然,除非自然而然地让这些方法可用于我正在使用的所有实体。我想这是一种罕见的情况。

相反,我是否应该创建一个基本的 IRepository 合同,其中包含尽可能多的——也许“尽可能少”更合适——常用方法(例如 GetByID())?所有的存储库都可以实现合约然后专门化。

还是走多个专用接口的路线更好,比如 ICreatable、IDeletable、IRetrievable 等?

【问题讨论】:

    标签: interface domain-driven-design dns repository


    【解决方案1】:

    我认为,如果接口中的方法被 all 存储库使用,您应该只创建一个基本的 IRepository。如果您有一些只实现部分方法的存储库,那么您应该选择更专业的接口。

    最后一个选项还可以让您在以后需要时更灵活地向存储库添加行为。如果您使用基本的 IRepository 接口并且必须向其添加新方法,那么您将必须更新所有存储库。如果您使用专用版本,则只需更新要应用此行为的存储库。

    【讨论】:

      【解决方案2】:

      通用接口是无用的,尤其是在 DDD 中,因为它们隐藏了其他明确的概念。想象一下您的命令处理程序/应用程序服务构造函数:

      public SomeCommandHandler(IRepository customerRepository, IRepository userRepository)
      

      并与此进行比较:

      public SomeCommandHandler(ICustomerRepository customerRepository, IUserRepository userRepository)
      

      后者更加明确,明确性是您通过 DDD 寻求的。此外,如果您的存储库具有所有相同的方法,那么您可能正在创建一个通常不适合 DDD 的 CRUD 数据访问层。

      【讨论】:

        【解决方案3】:

        我认为你应该使用 GenericRepository 和 Specification 模式。这样可以避免您在每个存储库中创建大量专门的方法。

        【讨论】:

        • 规范模式只用于查询,对吧?如果我们谈论其他功能,例如 Create() 和 Delete(),定义这些方法的通用存储库表明它们应该可用于所有实体。在大多数情况下可能不是一件好事。
        • 对 Create 和 Delete 方法的可用性不是一个好主意吗?还是什么?
        • 对。在电子商务域中,您应该能够删除 ShoppingCartItem 但不能删除已下订单。
        • 不明白 Repository 是怎么回事?还是您的意思是抽象级别?在您的情况下,您有 2 个聚合根:ShoppingCartItem 和 Order,并且您有 2 个存储库或一个通用存储库,没关系。您不应该为 Order 调用 delete 方法。仅此而已。
        • 我喜欢这样的设计,其中 Delete() 不仅不应该为 Order 调用,而且实际上不能调用,因为它不可用。这只有在两个单独的存储库中才有可能。如果我使用单个通用存储库,它将为所有实体公开 Delete(),无论 Delete() 是否对 Order 有意义。
        【解决方案4】:

        我完全同意耿祖的观点。 我已经在多个项目中使用了这种方法,在我作为解决方案架构师的角色中,我希望团队中的所有开发人员只编写必要和特定的代码。 我的经验是,您大多数时候都需要删除和查找方法等。您不希望这样做的情况不应该规定。结果将是需要删除的存储库都必须实现这一点,这将是冗余代码。所有这些 Delete 方法都可以由 GenericRepository 抽象类实现。该类可以针对 NHibernate 工作并将通用实体作为 T。然后,当您想要创建例如 UserRepository 时,它将从 GenericRepository 和 IUserRepository 继承。 其中 IUserRepository 从 IRepository 继承。我将添加一些代码:

            public class UserRepository : GenericNHibernateRepository<User, int>, IUserRepository
                {
                    #region IUserRepository Members
        
        
                    public User GetUserByEmail(string email)
                    {
                        ICriterion[] query = {Restrictions.Eq("Email", email)};
        
                        var list = GetByCriteria(query);
        
                        if (list.Count == 0)
        
                            return null;
                        else
                            return list[0];
                    }
        
                    #endregion
                }
        
        
            public interface IUserRepository : IRepository<User, int>
                {
                    User GetUserByEmail(string email);
                }
        
         public interface IRepository<T, ID> where T : IAggregateRoot
            {
                T GetById(ID id);
                List<T> GetAll();
                T Save(T entity);
                T SaveOrUpdate(T entity);
                void Delete(T entity);
                void Flush();
        }
        

        那么有什么好处。有巨大的! 正如您在 IRepository 接口中看到的那样,我们只允许 IAggregateRoot 拥有一个存储库(DDD 指南)。 所有存储库删除都将命名为 Delete(没有开发人员将其命名为 Remove 等,并且它仅在一个地方实现,即 GenericNHibernate 类)。 如果我团队中的开发人员想要为实体创建新存储库,唯一的代码是

        公共类 CustomerRepositoryNHibernate : GenericNHibernateRepository, ICustomerRepository {}

        然后您就可以读取、删除、查找功能...您的名字。不错。

        但是,如果您使用依赖注入和 IoC 框架,我可以说,例如 Windsor Castle,您可以创建一个工具来加载所有实现 IRepository 接口并位于组件中的存储库(例如 MyProject.Infrastructure 。数据)。这为您的团队提供了极大的推动力。

        首先,您的开发人员在 MyProject.Infrastructure.Data 中创建一个新的存储库,并使用上面示例中的代码行。

        第二次在应用程序启动时,Windsor Castle 设施将使其可用于注入。 所以你的开发人员可以继续编写一个控制器或服务类 IComnpanyRepository 作为构造函数参数及其全部。

        【讨论】:

        • 对于重用,毫无疑问通用存储库更好。尽管如此,对我来说,域和存储库之间的合同并没有反映域的业务意图这一事实有些不对劲。
        • 你总是从方法中剥离 IRepository 接口。但是该域使用的我的存储库接口(合同......)非常具有描述性和可视化他们正在做什么。使用 GetUserByEmail 方法查看上面的 IUserRepository。通常你会为每个存储库获得特定的方法。但是当涉及到 Delete 或 Load 或 Get 时,它们都做同样的事情,对一个实体实例进行操作。
        • 我更喜欢@S.Valmont 方法。存储库思维是关于减少代码行数。它通常会妨碍正确的 DDD 思维。您的数据访问只需要满足您的域执行的操作以及域需要进行的查询。我花了很多时间尝试编写华丽的多态存储库,但后来才意识到我的域实际上只做了几件事!此外,如果您要处理大量记录,则存储库或任何 ORM 方法都不太可能足够。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-19
        相关资源
        最近更新 更多