使域对象意识到数据访问层是不正确的?

时间:2009-01-22 04:04:23

标签: design-patterns orm domain-driven-design poeaa

我目前正致力于重写应用程序以使用从Domain层完全抽象数据库的Data Mappers。但是,我现在想知道哪个是处理Domain对象之间关系的更好方法:

  1. 直接在域对象
  2. 中从相关数据映射器调用必要的find()方法
  3. 将关系逻辑写入本机数据映射器(这是示例在PoEAA中的作用),然后在域对象中调用本机数据映射器函数。
  4. 在我看来,为了保留'Fat Model,Skinny Controller'的口头禅,域对象必须知道数据映射器(无论是他们自己还是他们可以访问其他映射器)系统)。此外,似乎选项2不必要地使数据访问层复杂化,因为它跨多个数据映射器创建表访问逻辑,而不是将其限制在单个数据映射器中。

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

    更新:这是我能想到的唯一两个解决域对象关系问题的解决方案。任何显示更好方法的例子都是受欢迎的。

4 个答案:

答案 0 :(得分:7)

是。问问自己为什么域名对象会知道这样的事情?甚至不是为什么,但如何?你打算将你的DAL注入你的Domain对象吗?

域名应该跟随SRP只是活了一切。当您遍历您的域时,您应该不知道这些属性是通过延迟加载填充还是从实例化中填充。

我编写了一个域模型,其中包含DAL对象,这是一个维护的噩梦。然后我学习了NHibernate,我的域名由POCO和我想要封装的各自的业务逻辑组成。

[编辑]

以下是更多信息。如果我试图解释它,我只会让自己难堪。我只能说作为用户的实现。这是一篇关于Domain Model management的精彩文章。你感兴趣的是拦截器和mixins的实现。

使用这些工具,您可以按如下方式编写员工类:

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);
    }
}

正如您所看到的,我的数据访问需求不会强加于我的域设计。

答案 1 :(得分:7)

我担心你有点误解了Repository模式的意图。

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

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映射器通信,或者在其他情况下调用存储过程,或者维护缓存为某些域对象。

这仍然无法解答您的问题。实际上,您可能有域对象具有委托给正确的存储库的save()和load()方法。我不认为这是正确的方法,因为持久性几乎从不是业务领域的一部分,它为您的域对象提供了多个改变的理由。

查看this related question以获得更多漫无目的。

回应对此答案的一些评论:

  

有效的批评。但是,我还是   然后混淆了如何得到一个单一的   域对象或集合   内部相关的域对象   现有域对象的上下文。    - gabriel1836

让我们说员工有很多技能。我认为Employee Repository调用Skill Repository没有错:

// 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;    
}

另一条路线不是调用技能资源库,而是让Employee Repository调用(在本例中)存储过程来加载技能的结果集,然后委托技能工厂获取技能列表。

  

我无法调用存储库   以及是否发出呼叫   数据映射器或加载对象   内存是它的关注,不是吗? -   gabriel1836

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

答案 2 :(得分:0)

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

从我所看到的,这正是设想的解决方案,它允许像Person这样的域对象将查询正确地委托给适当的数据映射器,同时让Domain Object和Data Mapper完全抽象化。

答案 3 :(得分:0)

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

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中需要依赖注入。