代码之家  ›  专栏  ›  技术社区  ›  Noah Goodrich

让域对象知道数据访问层是否不正确?

  •  8
  • Noah Goodrich  · 技术社区  · 16 年前

    我目前正在重写一个应用程序,使用数据映射器从域层完全抽象数据库。但是,我现在想知道处理域对象之间关系的更好方法是什么:

    1. 直接从域对象内的相关数据映射器调用必要的find()方法
    2. 将关系逻辑写入本机数据映射器(PoEAA中的示例通常会这样做),然后在域对象内调用本机数据映射器函数。

    或者在我看来,为了保留“胖模型,瘦控制器”的咒语,域对象必须知道数据映射器(无论是它们自己的映射器还是它们可以访问系统中的其他映射器)。此外,选项2似乎不必要地使数据访问层复杂化,因为它跨多个数据映射器创建表访问逻辑,而不是将其限制在单个数据映射器中。

    那么,让域对象知道相关的数据映射器并直接从域对象调用数据映射器函数是否不正确?

    这是我能设想的处理域对象之间关系问题的唯一两个解决方案。任何显示更好方法的示例都是受欢迎的。

    4 回复  |  直到 12 年前
        1
  •  7
  •   Scott Muc    16 年前

    对问问自己,为什么域对象会知道这样的事情?甚至不是为什么,而是如何?您要将DAL注入域对象吗?

    域应该遵循SRP,只需生活在其他方面。当您遍历您的域时,您应该不知道这些属性是通过延迟加载填充的,还是通过实例化生成的。

    我已经编写了一个域模型,其中包含DAL对象,维护起来真是一场噩梦。然后我了解了NHibernate和我的域由POCO和它们各自的业务逻辑组成,我想封装它们。

    [编辑]

    Domain Model management . 您感兴趣的是拦截器和mixin的实现。

    使用这些工具,您可以编写employee类,如下所示:

    public class Employee
    {
        public Employee()
        {
            Skills = new List<Skill>();
        }
    
        public string Name { get; set; }
        public IList<Skill> Skills { get; private set; }
    
        public void AddSkill(Skill skill)
        {
            // Perform Domain specific validation here...
    
            Skills.Add(skill);
        }
    }
    

    正如您所看到的,我的数据访问需求不会对我的域设计产生任何影响。

        2
  •  7
  •   Community leo1    7 年前

    恐怕您对存储库模式的意图有点误解。

    存储库的行为类似于特定域对象的内存集合,通常是聚合根:

    interface EmployeeRepository
    {
        List<Employee> retrieveBy(Criteria someCriteria);
        void store(Employee someEmployee);
        int countOf(Criteria someCriteria);
        // convenience methods
        Employee retrieveById(String id);
        Employee retrieveBySSN(String ssn);
    }
    

    这段代码的客户机不知道集合是否在内存中,就像单元测试一样,或者在某些情况下与ORM映射器交谈,或者在其他情况下调用存储过程,或者为某些域对象维护缓存。

    退房 this related question

    针对对该答案的一些评论:

    合理的批评。不过,我还是很担心 然后困惑于如何获得一个单一的 域对象或 在 现有域对象的上下文。 加布里埃尔1836

    假设一个员工有很多技能。我认为员工存储库这样调用技能存储库没有什么错:

    // assume EmployeeRepository talks to a DB via sprocs
    public Employee retrieveById(String id)
    {
        ResultSet employeeResultSet = this.database.callSproc("PROC_GetEmployeeById", 
            new Object[] { id });
    
        List<Skill> skills = 
            new SkillRepository().retrieveBy(new EqualsCriteria("EmployeeId", id));
    
        Employee reconstructed = new EmployeeFactory().createNew().
                                      fromResultSet(employeeResultSet).
                                      withSkills(skills).
                                      build();
    
        return reconstructed;    
    }
    

    另一种方法不是调用技能库,而是让员工库调用存储过程(在本例中)来加载技能的结果集,然后委托给技能工厂以获取技能列表。

    我不能打电话到存储库吗 以及是否向 数据映射器或加载对象 它关心的是记忆,不是吗? 加布里埃尔1836

    完全正确。我通常用这种方式模拟单元测试中的整个数据层。

        3
  •  0
  •   Noah Goodrich    16 年前

    在进一步阅读并寻找合适的模式后,我偶然发现 the Repository Pattern .

    据我所见,这正是预想中的解决方案,它允许像Person这样的域对象将查询正确地委托给适当的数据映射器,同时使域对象和数据映射器完全抽象。

        4
  •  0
  •   Humberto    14 年前

    我不同意,我认为域对象可以通过抽象工厂访问存储库。

    public class Client
    {
      public void ChangeZipCode(string zipCode)
      {
        // This method access memory or database depending on the factory context
        bool zipCodeExists = RepositoryFactory.Get<IZipCode>().Exists(zipCode);
        this.zipCode = zipCode;
      }
    }
    

    通过使用此模式,无需在代码中注入存储库接口 但只有存储库工厂。

    public abstract class RepositoryFactory
    {
      // Class operations
      private static _globalInstance;
      public static CreateGlobalInstance(RepositoryFactory factory)
      {
        _glocalInstance = factory;
      }
      public static I Get<I>()
      {
        return _globalInstance.Get<I>();
      }
      /////////////////////
    
      ///// this operation should be inherited by:
      ///// * NHibernateRepositoryFactory //
      ///// * Linq2SqlRepositoryFactory ////
      ///// * NUnitRepositoryFactory ///////      
      ///// it depends in your context ////////////////////////
      public abstract I GetRepository<I>();
    }
    

    我已经做了很多年了,在我的单元测试中没有任何问题。

    因此,依赖项注入仅在此类RepositoryFactory中是必需的。

    推荐文章