8

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

  1. 直接在域对象内从相关数据映射器调用必要的 find() 方法
  2. 将关系逻辑写入本机数据映射器(这是示例在 PoEAA 中倾向于执行的操作),然后在域对象中调用本机数据映射器函数。

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

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

更新:这是我可以设想的处理域对象之间关系问题的仅有的两种解决方案。欢迎任何显示更好方法的示例。

4

4 回答 4

7

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

域应该遵循 SRP 只是生活其他一切。当您遍历您的域时,您应该不知道这些属性是通过延迟加载填充的,还是从实例化中补充的。

我编写了一个包含 DAL 对象的域模型,维护起来简直就是一场噩梦。然后我学习了 NHibernate,我的域由我想要封装的 POCO 和它们各自的业务逻辑组成。

[编辑]

这里有更多信息。如果我试图解释它,我只会让自己尴尬。我只能以用户的身份谈论实现。这是一篇关于域模型管理的精彩文章。您感兴趣的是拦截器和混入的实现。

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

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

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

于 2009-01-22T04:19:44.023 回答
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 映射器交谈,或者在其他情况下调用存储过程,或者为某些域对象维护缓存.

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

查看此相关问题以了解更多内容。

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

一个有效的批评。但是,我仍然对如何在现有域对象的上下文中获取单个域对象或相关域对象的集合感到困惑。– 加布里埃尔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

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

于 2009-01-22T22:23:00.157 回答
0

在进一步阅读并搜索了合适的模式之后,我偶然发现了 Repository Pattern

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

于 2009-01-22T17:09:26.327 回答
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 中才需要依赖注入。

于 2010-05-06T14:55:23.843 回答