DDD中的数据访问层设计

发布于 2024-12-03 16:41:26 字数 1494 浏览 1 评论 0原文

请原谅我糟糕的英语。

好的,我现在正在考虑 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() 方法的公共 getters/setters。但停下来。我不希望我的 POCO 有这样的功能!没有相关的业务规则。必须这样做才能保存/恢复对象。

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

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

我做错了吗?我需要阅读更多内容吗?关于某些模式,ORM 可能吗?

Excuse me for my poor English.

Ok, I'm thinking about DDD approach now and it sounds great but... There is one little question about it. DDD says that the domain model layer is totally decoupled from the data access layer (and all other layers). So when the DAL will save some business object it will have access to public properties of this object only. Now the question:

How can we guarantee (in general) that a set of public data of an object
is all we need to restore the object later?

Example

We have the following business rules:

  1. User and domain must be provided for the business object on create.
  2. User and domain cannot be changed after object creation.
  3. The business object have the Email property which looks like "user@domain".

Here is a pure POCO which describes those rules:

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

So at some moment the DAL will save this object to the external storage (i.e. SQL database). Obviously, the DAL will save the "Email" property to the associated field in DB. Everything will work just fine until the moment when we'll ask the DAL to restore the object. How the DAL can do this? The object must have a public setter for the "Email" field at least. Something like

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

Actually, the object will have public getters/setters for both "User" and "Domain" fields and method GetEmail(). But stop. I don't want my POCO to have such functionality! There are no business rules for it. This must be done for the ability to save/restore the object only.

I see another option. The ORM which is a part of the DAL could be asked to store all of the private fields needed to restore the object. But this is impossible if we want to keep the domain model separated from the DAL. The DAL cannot rely on certain private members of the business object.

The only workaround I can see is to have some system-level instrument which can create the dump of the object for us and can restore object from this dump anytime. And the DAL must put this dump to the storage in addition to public properties of the object. So when the DAL needs to restore the object from storage it will use the dump for this. And the public properties saved to storage can be used when the DAL is performing operations that don't need the object to be instantiated (i.e. most of link2sql queries).

Am I doing it wrong? Do I need read more? About some patterns, ORM's maybe?

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

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

发布评论

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

评论(2

追星践月 2024-12-10 16:41:26

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

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

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

  • 字段可以是私有的,也可以是只读的
  • public 构造函数仅由客户端代码使用,而不是由 DAL 使用。
  • 不需要属性 getter 和 setter
  • 业务对象几乎 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接口属于Domain,应该尽可能基于通用语言。另一方面,存储库实现属于 DAL(依赖倒置原则)。

I think you got this part wrong:

I see another option. The ORM which is a part of the DAL could be
asked to store all of the private fields needed to restore the object.
But this is impossible if we want to keep the domain model separated
from the DAL. The DAL cannot rely on certain private members of the
business object.

Domain model does not depend on DAL. Its the other way around, DAL depends on Domain model.
ORM has intimate knowledge of Domain Objects, including private fields. There is absolutely nothing wrong with that. In fact this is the best way to implement persistent-ignorance in DDD. This is how the Domain class can look like. Note that

  • fields can be private and readonly
  • public Constructor is only used by client code, not by DAL.
  • no need for property getters and setters
  • Business object is almost 100% ignorant of persistence issues

The only thing DAL/ORM needs is private parameterless consturctor:

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

And the magic happens in ORM. Hibernate can restore this object from database using this mapping file:

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

Another aspect of persistence-ignorant domain code is DDD Repository:

Definition: A Repository is a mechanism for encapsulating storage,
retrieval, and search behavior which emulates a collection of objects.

Repository interface belongs to Domain and should be based on Ubiquitous Language as much as possible. Repository implementation on the other hand belongs to DAL (Dependency Inversion Principle).

冷︶言冷语的世界 2024-12-10 16:41:26
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)

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

One simple solution is to just have your DAL call new BusinessObject(email)

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