让域对象了解数据访问层是不正确的吗?

发布于 2024-07-12 10:25:33 字数 457 浏览 7 评论 0原文

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

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

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

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

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

I am currently working on rewriting an application to use Data Mappers that completely abstract the database from the Domain layer. However, I am now wondering which is the better approach to handling relationships between Domain objects:

  1. Call the necessary find() method from the related data mapper directly within the domain object
  2. Write the relationship logic into the native data mapper (which is what the examples tend to do in PoEAA) and then call the native data mapper function within the domain object.

Either it seems to me that in order to preserve the 'Fat Model, Skinny Controller' mantra, the domain objects have to be aware of the data mappers (whether it be their own or that they have access to the other mappers in the system). Additionally it seems that Option 2 unnecessarily complicates the data access layer as it creates table access logic across multiple data mappers instead of confining it to a single data mapper.

So, is it incorrect to make the domain objects aware of the related data mappers and to call data mapper functions directly from the domain objects?

Update: These are the only two solutions that I can envision to handle the issue of relations between domain objects. Any example showing a better method would be welcome.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

╰◇生如夏花灿烂 2024-07-19 10:25:33

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

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

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() 方法。 我认为这不是正确的方法,因为持久性几乎从来都不是业务领域的一部分,并且它为您的领域对象提供了不止一个更改的理由。

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

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

有效的批评。 然而我还是
然后对如何获得一个感到困惑
域对象或集合
相关域对象在
现有域对象的上下文。
– gabriel1836

假设一名员工拥有多种技能。 我认为员工存储库像这样调用技能存储库没有任何问题:

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

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

I'm afraid you've slightly misunderstood the intention of the Repository pattern.

The Repository is meant to behave like an in-memory collection of a particular domain object, usually an aggregate root:

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

Clients of this code have no idea whether the collection is in memory, like you'd have for unit-testing, or talking to an ORM mapper in some cases, or calling a stored procedure in others, or maintaining a cache for certain domain objects.

This still doesn't answer your question. In fact, you could potentially have domain objects have save() and load() methods that delegate to the right repository. I don't think this is the right way to go because persistence is almost never a part of the business domain, and it gives your domain object more than one reason to change.

Check out this related question for more rambling.

In response to some comments on this answer:

A valid criticism. However, I'm still
confused then on how to get a single
domain object or a collection of
related domain objects when within the
context of an existing domain object.
– gabriel1836

Let's say an Employee has many Skills. I see nothing wrong with the Employee Repository calling a Skill Repository like this:

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

Another route is instead of calling the Skill Repository, have the Employee Repository call the, in this example, stored procedure to load the result set for skills, then delegate to a Skill Factory to get the list of Skills.

Can't I make a call to the Repository
and whether it issues a call to the
data mapper or loads the object
in-memory is its concern, isn't it? –
gabriel1836

Exactly right. I usually mock out the entire data tier in my unit tests this way.

身边 2024-07-19 10:25:33

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

域应该遵循 SRP,只有其他一切都可以。 当您遍历域时,您应该不知道这些属性是通过延迟加载填充的,还是通过实例化进行水化的。

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

[编辑]

这里有更多信息。 如果我试图解释的话,我只会让自己难堪。 我只能以用户的身份谈论实现。 这是一篇关于域模型管理的精彩文章。 您感兴趣的是拦截器和 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);
    }
}

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

Yes. Ask yourself why would a domain object know about such a thing? Not even why, but how? You're going to inject your DAL into your Domain object?

The domain should follow SRP just live everything else. When you traverse your domain you should be unaware if those properties were populated via lazy loading, or hydrated from instantiation.

I've written a domain model that had DAL objects in them and it was a nightmare to maintain. Then I learned NHibernate and my domain consists of POCO's and their respective business logic that I want to encapsulate.

[Edit]

Here's more info. I would only embarrass myself if I tried to explain it. I can only speak of the implementation as a user. Here's a great article on Domain Model management. What you are interested in is the implementation of interceptors, and mixins.

With those tools you can write an employee class as follows:

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

As you can see, my data access needs don't impose on my Domain design whatsoever.

嗳卜坏 2024-07-19 10:25:33

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

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

I disagree, I think domain object can access Repositories through Abstract Factory.

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

By using this pattern, it is not necessary to inject repository interfaces all over your code
but only the repository factory.

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

I have been doing it for years without any problems in my unit tests.

Therefore dependency injection is required only in this class RepositoryFactory.

北风几吹夏 2024-07-19 10:25:33

在进一步阅读并搜索合适的模式后,我偶然发现了存储库模式

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

After having done some further reading and searching for an appropriate pattern, I stumbled upon the Repository Pattern.

From what I can see this is precisely the envisioned solution that allows for Domain Objects like Person to properly delegate queries to the appropriate Data Mapper while leaving the Domain Object and Data Mapper completely abstracted.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文