代码之家  ›  专栏  ›  技术社区  ›  Tim Lytle

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

  •  11
  • Tim Lytle  · 技术社区  · 14 年前

    我第一次使用doctrine2,但我认为这个问题足够通用,不依赖于特定的ORM。

    数据映射器模式中的实体是否应该知道-和 使用 -映射器 ?

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

    如果我正在处理来自外部源的数据-例如 User 有许多 Messages -外部源只提供最新的少数实体(如RSS提要),如何 $user->addMessage($message) 检查是否存在重复项,除非它知道映射器,或者它通过集合“搜索”(似乎是一种效率低下的操作)。

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

    如果我有大量收藏-再次 用户 与许多 信息 -怎么能 用户 实体是否为集合提供限制和分页,而不实际代理映射器调用?

    同样,控制器或事务脚本或使用实体的任何内容都可以直接使用映射器来检索 用户 信息 受计数、日期范围或其他因素的限制——但这也会导致代码重复。

    答案是使用存储库并使实体知道它们吗? ?(至少对于doctrine2,以及其他表单使用的任何类似概念)在这一点上,实体仍然相对地与映射器分离。

    3 回复  |  直到 14 年前
        1
  •  8
  •   jsuggs    14 年前

    规则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。您的域实体对象只是那些对象……它们不应该知道它们来自何处,域模型只是管理它们并将它们传递给管理和操作它们的各种函数。

    回首过去,我不确定我是否完全回答了你的问题。但是,我不认为实体本身应该有任何访问映射器的权限。创建服务/存储库/任何操作对象的工具,并在这些功能中使用适当的技术…

    从一开始也不要过度设计。让您的域专注于它的目标,并在性能实际上是一个问题时重构。

        2
  •  1
  •   Otávio Décio    14 年前

    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());
    

    连接被反转。

    因此,有可能使实体完全不知道映射器。

        3
  •  1
  •   Geekster    14 年前

    不。

    原因如下:信任。您不能信任数据以对系统有利。您只能信任系统对数据进行操作。这是编程逻辑的基础。

    假设数据中有一些令人讨厌的东西,它是为XSS设计的。如果一个数据块正在执行操作或者进行了评估,那么XSS代码就会融入其中,从而打开一个安全漏洞。

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