【问题标题】:How to construct domain model from persistence store? #DDD如何从持久存储构建域模型? #DDD
【发布时间】:2021-02-14 02:58:12
【问题描述】:

我正在尝试学习领域驱动设计 (DDD)。而且我不明白如何从持久存储构建域模型。我正在制作没有公共设置器的域模型,以保持我的状态安全和一致,但另一方面,要从持久性存储构建它们,我需要拥有这些设置器。在“实施DDD”中,只要我使用ORM工具就可以。但是如果我不想使用 ORM,实际上我想使用 NoSQL 怎么办。我可能会使用事件溯源。如果大家有更好的建议。在这种情况下,最佳做法是什么。

【问题讨论】:

    标签: model architecture domain-driven-design ddd-repositories ddd-service


    【解决方案1】:

    如何从持久化存储中构造领域模型?

    我的建议:如果您的域有点复杂,请不要这样做。

    数据库模型是为存储数据而设计的模型。如果您由此生成域模型,则结果将是 anemic model: 没有任何行为的实体。
    考虑到 DDD 原则设计的领域模型将产生一个由实体和具有行为的值对象组成的丰富模型。这些行为将帮助您实现属于特定有界上下文的用例。

    话虽如此,如果您的有界上下文只是 CRUD 而没有域复杂性,那么这是一种完全有效的方法。请记住,DDD 不会强制选择任何架构。根据有限的上下文复杂性,您可能需要使用或不使用大量战术模式。

    【讨论】:

      【解决方案2】:

      我相信您将域模型与数据传输对象 (DTO) 混合在一起。存储库不使用(引用)域模型,它使用 DTO。

      通常您会将 IRepository 实例传递给业务模型对象的 ctor,然后模型本身可以使用存储库来获取数据(以 DTO 的形式),直接设置其私有属性,或者在需要时使用其方法来验证数据按业务。您传递 IRepository 实例是因为您可能还需要保存数据,而不仅仅是加载它。因此,为了让业务对象能够知道如何保存经过验证的数据,它需要引用存储库实例。

      或者,如果您想对此非常纯粹,并且不传递存储库实例,并将加载/保存留给应用程序层,那么您可以在应用程序层的某处调用 repository.Getxxx 并传递数据(作为 DTO)到商业模式负责人。

      【讨论】:

      • 谢谢,您是在建议 Active Record 模式?我正在使用数据映射器。不过,这可能是个不错的选择。
      • @UuganboldTsegmed 不,我建议使用存储库模式,它用于将业务模型层与持久性(存储)层解耦。
      • @UuganboldTsegmed 顺便说一句,当您谈论数据映射器时,据我了解,它应该只是一个将数据从域模型映射到 DTO(存储库数据)的中间层(中介服务),反之亦然,无论数据是1on1映射,还是领域模型与数据库结构不同(对象关系阻抗不匹配)。
      【解决方案3】:

      首先,你使用什么数据库并不重要,所以你可以使用 SQL 数据库或 NoSQL,这同样适用于 ORM 或低级客户端。

      您的存储库需要做的是收集数据并将其传递给您的域模型的构造函数,因此不需要公共设置器。我用一个简单的例子添加了一些伪代码:

      class UserEntity:
        public function constructor(name, email)
          this._set_name(name)
          this._set_email(email)
      
        private function _set_name(name)
          this.name = name
      
        private function _set_email(email)
          this.email = email
      
      
      class Repository:
        public static function get_by_email(a_email)
          row = client.sql("SELECT * FROM foo WHERE email=%", a_email)
          return UserEntity(name=row["name"], email=row["email"])
      

      【讨论】:

      • 是的,有可能,但它违反了与 setter 相同的封装。您如何看待使用像休眠这样的反射设置私有字段
      • 您能否详细说明“违反封装”的含义?
      • 我遵循 Uuganbold 的观点,即通过构造函数对域模型进行水合可能会破坏封装。假设有某些私有属性只能通过封装一些业务需求的方法来更改。使用构造函数对该属性进行水合可能会使其具有不满足这些业务要求的值。
      • @Thogor 如果数据不符合业务要求,它是如何持久化的?数据应在持久化之前进行验证。即使在业务需求发生变化的情况下。并且持久化的数据变得无效,验证数据的任务不应该是存储库的任务,而是域模型本身,因为它是它的数据。
      • @fathineos 在您的示例中,存储库方法知道域模型,但应该反过来,域模型应该使用(引用)存储库来获取其数据(以及其他 CRUD 操作)。另外,如果您的存储库方法是静态的,您将如何在测试中模拟它?
      猜你喜欢
      • 2012-12-11
      • 2017-03-27
      • 2018-08-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-19
      • 1970-01-01
      • 2014-08-06
      相关资源
      最近更新 更多