【问题标题】:DDD - Entity creation and validation responsibilityDDD - 实体创建和验证责任
【发布时间】:2012-02-28 00:41:50
【问题描述】:

最近几天我对 DDD(领域驱动设计)很感兴趣,但我无法弄清楚谁创建和验证实体的职责。我将打破这个问题以涵盖不同的场景。

  1. 常规实体(可能带有值对象)。作为示例,让我们以电子邮件标识的用户为例。我有一个 UserFactory,它接收一组数据(可能来自表单 POST),并返回一个新的 UserEntity。工厂是否应该验证数据的完整性(例如:作为电子邮件给出的字符串是真实的电子邮件,密码字段 1 和字段 2 中的密码匹配等)?工厂是否应该验证不存在这样的用户(我们不想使用相同的电子邮件注册两个用户)?如果是,它应该自己做还是使用 UserRepository?

  2. 聚合实体。假设我们有一个 Post 实体和 Comments 实体。我想获得所有 cmets 的 post 12,所以我做了类似的事情

    $post = $postRepository->getById(12);

getById 应该如何实现?像这样:

public function getById($id) {
    $postData = $this->magicFetchFromDB($id);
    $comments = (new CommentRepository())->getForPost(12);
    return new PostEntity($postData, $comments);
}

或者可能是负责懒惰创建其 cmets 的帖子,例如:

class PostEntity {
    public function getComments() {
        if(is_null($this->_comments)) $this->_comments = (new CommentRepository())->getForPost($this->_id);
        return $this->_comments;
    }
}

? 我在这里很迷茫,没有足够的信息与 PHP 中的 DDD 示例,所以任何帮助将不胜感激!

非常感谢, skwee。

【问题讨论】:

    标签: php domain-driven-design


    【解决方案1】:
    • 工厂应该关心创建实体。我个人更喜欢在我的视图和模型层中都进行验证。我会使用一些库,比如 jQuery 的验证插件,在客户端进行一些 essential 验证(比如检查必填字段是否有数据)。然后对模型进行“硬核”验证。 我这样做是通过使用一个所有实体都扩展的简单 BaseEntity 抽象类,既然你问了一个例子,这里是:

      abstract class BaseEntity {
          public function isValid();
      }         
      
      class MyEntity extends BaseEntity {
           public function isValid() {
               //actual validation goes here
           }
       }
      

      您还可以使用带有一些基本验证方法的静态助手类:

      class ValidationHelper {
          public static function isValidPhonenumber($value) {
              //check valid phonenumber, using a regex maybe
          }
      
          public static function isAlphanumeric($value) {
              //check for letters and numbers only
          }
      }
      

      许多人反对静态方法,因为它们会破坏单元测试,但在这种情况下,它们非常基本,并且没有外部依赖项,因此使它们“更安全”。

    • 在检查已经存在的实体时,您可以通过在添加/更新之前查询您的数据库以查看该实体是否已经存在,或者(我喜欢这样做)您可以添加unique 索引到那些不能在数据库中重复的列,然后将创建或更新查询包装在 try-catch 块中(如果两个用户有相同的电子邮件,查询将引发唯一约束冲突例如),然后显示正确的错误消息

    • 关于您的最后一个问题,它归结为偏好问题。如果您的数据库将在 1 分钟内获得一百万次点击,那么最好使用延迟加载来避免在需要之前获取不必要的数据。但是,如果您的数据库相对较小,您可以完美地使用预加载而不牺牲太多性能。 同样,这是个人喜好问题。

    希望这种漫无边际的事情是有道理的,干杯!

    【讨论】:

    • 感谢您的评论! A)我不确定是否在实体中进行验证。你会考虑验证验证码是业务逻辑的一部分吗?如果我有管理面板来手动添加用户而不需要验证码怎么办? B) 是的,我可以,但问题是谁的责任?验证层?工厂?存储库? C) 好的,但是如果我的用户的帖子包含 cmets,加载所有这些数据以显示用户个人资料页面是否明智?
    • a) 这真的取决于您的需求,我发现使用辅助类在实体本身中进行验证“更容易”。但是添加额外的验证层是非常好的。 b)我会说这是验证层的责任。如果两个帐户不能有相同的号码,而您尝试用一个已经存在的号码添加一个,这意味着该实体是无效,或者至少我是这样认为的。
    • c) 这也取决于你想要什么。如果您要在个人资料页面上一次显示所有这些信息,那么可以,一次调用即可加载所有信息。但是,如果您只显示帖子,并且当您单击一个帖子时会显示 cmets,那么最好在那一刻加载 cmets,也许使用 ajax
    • b) 好吧,那么工厂只是验证数据(通过不同的层,验证)和返回有效实体(如果实体无效则返回错误)之间的粘合层。 c)它有点复杂,例如假设我只想显示用户详细信息并按需加载帖子(ajax),然后用户实体需要了解 PostRepository,这会将代码联系在一起(恕我直言,这是不好的)。也许我应该研究观察者模式。
    【解决方案2】:

    最好和最简单的方法是使用 Doctrine2。也许最初的几个小时会很艰难,但一旦你掌握了教义2,所有这些关系和聚合都是小菜一碟。

    您可以在http://giorgiosironi.blogspot.com/ 上或通过谷歌搜索找到大量关于 PHP 和 DDD 或 Doctrine2 的信息。

    RE:验证 - 考虑到单一职责原则,我们使用验证对象。验证可能很复杂,可能需要其他存储库或实体,因此最好将其与实际实体分开 - 验证的主题以避免创建臃肿的对象。您可以使用访问者或规范设计模式。

    这里还有很多关于这些主题的其他帖子 - 尝试使用上述关键字。

    【讨论】:

    • 我当时用的学说,不是很喜欢,又大又重。不管是在 Doctrine2 之前,我可能会再给 Doctrine 一个机会。但是,我仍然对正确的做事方式比找到现成的解决方案更感兴趣。至于验证,感谢您提供 SRP!我完全忘记了,不确定是否应该插入另一层验证,现在我确定我需要:)
    • Doctrine 基于 Active Record 模式,Doctrine2 正是您更容易实现 DDD 所需要的。正如我在另一篇类似的帖子中所写的那样 - 自制解决方案通常以维护噩梦而告终,并且聚合根变得非常手动。通常每个 AR 都是根据开发者制作的不同方式完成的。去过那里,相信我,你不想走那条路……
    • 我有“重新发明轮子”综合症。如果我被要求在时间紧迫的工作场所实施 DDD,我可能会选择 Doctrine。由于我的问题是从我的个人项目中提出的,我没有严格的截止日期,我更喜欢通过实施这些东西来尽可能多地学习。我有一个很好的解决方案 ATM,但我担心它会在我试图使其复杂化时立即损坏,所以我正在考虑 Doctrine。此外,AFAIK Doctrine 不支持非 DB 存储(即基于文件或 noSQL 数据库),这可能是未来的问题(我确信编写驱动程序不是问题)。
    • 我在我的 MongoDB 研究项目中使用 Doctrine,我认为还有一个 CouchDB 驱动程序 - github.com/doctrine/couchdb-odm
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多