首页 文章

使用数据映射器模式,实体(域对象)是否应该了解Mapper?

提问于
浏览
11

我是第一次使用Doctrine2,但我认为这个问题非常通用,不依赖于特定的ORM .

Should the entities in a Data Mapper pattern be aware - and use - the Mapper

我有几个具体的例子,但它们似乎都归结为同样的一般性问题 .

如果我正在处理来自外部源的数据 - 例如 User 有很多 Messages - 并且外部源只提供最新的几个实体(如RSS提要),那么 $user->addMessage($message) 如何检查重复项,除非它知道Mapper,或它'searches'通过集合(似乎是一个低效的事情) .

当然,控制器或事务脚本可以在将消息添加到用户之前检查重复项 - 但这似乎不太正确,并且会导致代码重复 .

如果我有一个大型集合 - 再次 User 与许多 Messages - User 实体如何在不实际代理Mapper调用的情况下为集合提供限制和分页?

同样,Controller或Transaction Script或使用Entity的任何东西都可以直接使用Mapper来检索受计数,日期范围或其他因素限制的 User 的集合 - 但这也会导致代码重复 .

Is the answer using Repositories and making the Entity aware of them ? (至少对于Doctrine2,以及其他ORM使用的类似概念 . )此时,实体仍然与Mapper相对分离 .

3 回答

  • 8

    规则#1:保持您的域模型简单明了 .

    首先,不要过早地优化某些东西,因为你认为它可能效率低下 . 构建域以使对象和语法正确流动 . 保持接口干净:$ user-> addMessage($ message)干净,精确且明确 . 在引擎盖下,您可以使用任意数量的模式/技术来确保维护完整性(缓存,查找等) . 您可以利用服务来协调(复杂)对象依赖性,这可能是过度的,但这是一个基本的示例/想法 .

    class User
    {
      public function addMessage(Message $message)
      {
         // One solution, loop through all messages first, throw error if already exists
         $this->messages[] $message;
      }
      public function getMessage()
      {
         return $this->messages;
      }
    }
    class MessageService
    {
      public function addUserMessage(User $user, Message $message)
      {
         // Ensure unique message for user
         // One solution is loop through $user->getMessages() here and make sure unique
         // This is more or less the only path to adding a message, so ensure its integrity here before proceeding 
         // There could also be ACL checks placed here as well
         // You could also create functions that provide checks to determine whether certain criteria are met/unmet before proceeding
         if ($this->doesUserHaveMessage($user,$message)) {
           throw Exception...
         }
         $user->addMessage($message);
      }
      // Note, this may not be the correct place for this function to "live"
      public function doesUserHaveMessage(User $user, Message $message)
      {
         // Do a database lookup here
         return ($user->hasMessage($message) ? true
      }
    }
    class MessageRepository
    {
      public function find(/* criteria */)
      {
         // Use caching here
         return $message;
      }
    }
    
    class MessageFactory
    {
       public function createMessage($data)
       {
         //
         $message = new Message();
         // setters
         return $message;
       }
    }
    
    // Application code
    $user = $userRepository->find(/* lookup criteria */);
    $message = $messageFactory->create(/* data */);
    // Could wrap in try/catch
    $messageService->sendUserMessage($user,$message);
    

    一直在使用Doctrine2 . 您的域实体对象就是那些对象......他们不应该知道它们来自何处,域模型只管理它们并将它们传递给管理和操作它们的各种函数 .

    回顾过去,我不确定我是否完全回答了你的问题 . 但是,我不认为实体本身应该可以访问映射器 . 创建服务/存储库/无论在对象上操作什么,并在这些功能中使用适当的技术......

    从发病开始也不要过度工程化 . 当性能实际上是一个问题时,让您的域名专注于其目标和重构 .

  • 1

    IMO,一个实体应该忘记它来自哪里,谁创建它以及如何填充其相关实体 . 在ORM中我使用(我自己的)我能够定义两个表之间的连接并通过指定(在C#中)来限制其结果:

    SearchCriteria sc = new SearchCriteria();
    sc.AddSort("Message.CREATED_DATE","DESC");
    sc.MaxRows = 10;
    results = Mapper.Read(sc, new User(new Message());
    

    这将导致一个限制为10个项目的连接,按日期创建消息排序 . 消息项目将添加到每个用户 . 如果我写:

    results = Mapper.Read(sc, new  Message(new User());
    

    联接是相反的 .

    因此,可以使实体完全不知道映射器 .

  • 1

    没有 .

    这就是原因:信任 . 您不能相信数据会对系统带来好处 . 您只能信任系统对数据采取行动 . 这是编程逻辑的基础 .

    让我们说一些讨厌的东西插入到数据中,它是用于XSS的 . 如果数据块正在执行操作或者如果它被评估,那么XSS代码将被混合到事物中并且它将打开安全漏洞 .

    左手不要知道右手做什么! (主要是因为你不想知道)

相关问题