【问题标题】:Data access layer design in DDDDDD中的数据访问层设计
【发布时间】:2011-11-13 23:38:04
【问题描述】:

对不起,我的英语很差。

好的,我现在正在考虑 DDD 方法,这听起来不错,但是... 关于它有一个小问题。 DDD 表示域模型层与数据访问层(以及所有其他层)完全解耦。因此,当 DAL 将保存一些业务对象时,它将只能访问该对象的公共属性。现在的问题:

我们如何(通常)保证一个对象的一组公共数据 我们只需要稍后恢复对象吗?

示例

我们有以下业务规则:

  1. 必须在创建时为业务对象提供用户和域。
  2. 创建对象后无法更改用户和域。
  3. 业务对象具有类似于“user@domain”的电子邮件属性。

这是一个描述这些规则的纯 POCO:

public class BusinessObject
{
    private string _user;
    private string _domain;

    public BusinessObject(string user, string domain)
    {
        _user = user;
        _domain = domain;
    }

    public string Email
    {
        get { return _user + "@" + _domain; }
    }
}

所以在某个时刻,DAL 会将此对象保存到外部存储(即 SQL 数据库)。显然,DAL 会将“Email”属性保存到 DB 中的相关字段中。在我们要求 DAL 恢复对象之前,一切都会正常工作。 DAL 如何做到这一点?该对象必须至少具有“电子邮件”字段的公共设置器。类似的东西

public string Email
{
    set
    {
        string[] s = value.Split("@");
        _user = s[0];
        _domain = s[1];
    }
}

实际上,该对象将为“用户”和“域”字段以及 GetEmail() 方法提供公共 getter/setter。但是停下来。我不希望我的 POCO 有这样的功能!它没有商业规则。必须这样做才能仅保存/恢复对象。

我看到了另一种选择。可以要求作为 DAL 一部分的 ORM 存储恢复对象所需的所有私有字段。但是,如果我们想让域模型与 DAL 分离,这是不可能的。 DAL 不能依赖业务对象的某些私有成员。

我能看到的唯一解决方法是拥有一些系统级工具,它可以为我们创建对象的转储,并可以随时从该转储中恢复对象。除了对象的公共属性外,DAL 还必须将此转储存储到存储中。因此,当 DAL 需要从存储中恢复对象时,它将为此使用转储。当 DAL 执行不需要实例化对象的操作(即大多数 link2sql 查询)时,可以使用保存到存储中的公共属性。

我做错了吗?我需要阅读更多内容吗?关于一些模式,也许是 ORM?

【问题讨论】:

    标签: orm domain-driven-design poco data-access-layer


    【解决方案1】:

    我认为你把这部分弄错了:

    我看到了另一种选择。作为 DAL 一部分的 ORM 可以是 要求存储恢复对象所需的所有私有字段。 但如果我们想保持领域模型分离,这是不可能的 来自 DAL。 DAL 不能依赖于 业务对象。

    域模型不依赖于 DAL。相反,DAL 依赖于域模型。 ORM 对领域对象有深入的了解,包括私有领域。这绝对没有错。事实上,这是在 DDD 中实现持久无知的最佳方式。这就是 Domain 类的样子。请注意,

    • 字段可以是私有的和只读的
    • public Constructor 仅供客户端代码使用,DAL 不使用。
    • 不需要属性获取器和设置器
    • 业务对象几乎 100% 不了解持久性问题

    DAL/ORM 唯一需要的是私有无参数构造函数:

    public class BusinessObject {
        private readonly string _user;
        private readonly string _domain;
    
        private BusinessObject(){}
    
        public BusinessObject(string user, string domain) {
            _user = user;
            _domain = domain;
        }
    
        public string Email {
            get { return _user + "@" + _domain; }
        }
    }
    

    奇迹发生在 ORM 中。 Hibernate 可以使用这个映射文件从数据库中恢复这个对象:

    <class name="BusinessObject" table="BusinessObjects">
        ...
        <property name="_user" column="User" />
        <property name="_domain" column="Domain" />
        ...
    </class>
    

    持久性无知域代码的另一个方面是DDD Repository

    定义:Repository 是一种封装存储的机制, 模拟对象集合的检索和搜索行为。

    Repository 接口属于Domain,应尽可能基于Ubiquitous Language。另一方面,存储库实现属于 DAL (Dependency Inversion Principle)。

    【讨论】:

    • 所以,也就是说,ORM 使用了一些“肮脏的技巧”(例如反射)来让您在域代码中实现持久性无知。
    • 感谢您的回答!现在我看到我必须更多地了解不同的 ORM。但我仍然认为这种方法还不够好。每次我向 POCO 添加额外的私有字段时,我都必须考虑如何存储它。我什至认为最好在业务对象中维护这种依赖关系(例如,使用基于属性的映射),因为它首先出现在那里。但也许我只是想做比实际更复杂的事情。
    • @akakey:基于属性的映射违背了持久性无知的目的。添加新字段很可能需要更改域、DAL 和 UI。但这并不意味着您必须将所有这些问题捆绑在一个类中。除非您想遵循此建议:stackoverflow.com/questions/7365309/…
    • 问题是当我阅读 DDD 时,我没有看到 UI 或任何其他组件依赖于域的 internal 实现。它们仅依赖于 public 接口。没关系。唯一真正依赖于域内部实现的组件是 DAL。无论此依赖项存在于何处(在 XML 文件或属性属性中)。
    • @Dmitry:现在我知道你是对的。 DAL 依赖于域的内部实现,就像它依赖于任何其他公共接口一样。因此,当公共接口发生变化时,该接口的所有客户端也必须更改其逻辑。
    【解决方案2】:
    public class BusinessObject
    {
        private string _user;
        private string _domain;
    
       public BusinessObject(string email)
       {
          string[] s = value.Split("@");
          _user = s[0];
          _domain = s[1];    
       } 
    
       public BusinessObject(string user, string domain)
        {
            _user = user;
            _domain = domain;
        }
    
        public string Email
        {
            get { return _user + "@" + _domain; }
        }
    }
    

    一个简单的解决方案是让您的 DAL 调用 new BusinessObject(email)

    【讨论】:

    • 我认为这是相同的东西。现在我可以创建提供电子邮件的业务对象,但我仍然没有合适的业务规则。
    猜你喜欢
    • 2015-11-14
    • 2010-12-03
    • 2012-06-06
    • 2010-10-11
    • 1970-01-01
    • 2010-11-06
    • 2015-02-27
    相关资源
    最近更新 更多