【问题标题】:Where should I place DO property checks that require input from other objects?我应该在哪里放置需要其他对象输入的 DO 属性检查?
【发布时间】:2013-07-12 04:46:17
【问题描述】:

我试图了解域对象、数据映射器和服务的结构,但是我很难准确地掌握什么去哪里。

This 的问题非常相似,但没有有用的答案。

需要来自多个对象的属性的方法是否应该进入;

  1. 域对象,或
  2. 服务类,或
  3. 也不...

我担心创建一个繁重的 Service 类,但这种方法似乎比同时编辑 DO 和 DataMapper 更简单。

final class UserService extends ServiceAbstract
{
    public function user_is_admin($id)
    {
        // Build the user object
        $userMapper = $this->dataMapperFactory->build('user');
        $user = $this->domainObjectFactory->build('user');
        $user->id = $id;
        $userMapper->fetch($user);

        // Build the admin usergroup object
        $usergroup = $this->domainObjectFactory->build('usergroup');
        $usergroupMapper = $this->dataMapperFactory->build('usergroup');
        $usergroup->id = ADMIN_USERGROUP_ID;
        $usergroupMapper->fetch($usergroup);

        // Perform the actual check
        if(in_array($user->id, $usergroup->aMembers))
        {
            return true; 
        }
    }
}

【问题讨论】:

  • 为什么要打破$user$userGroup 实例的封装?
  • @tereško 请原谅我的无知,但我不确定我是否理解你的问题,我在破坏什么?
  • @tereško 我相信这有点离题,但我想你希望我写 $user->setId($id);代替 $user->id = $id;?
  • 这就是为什么它是评论而不是答案的一部分。使用这种方法意味着两种可能性之一:要么您使用public 可见性作为域对象的参数,要么您正在使用魔术__set() 方法。这两者都倾向于对应用产生威慑作用。魔术方法往往会成为复杂的黑洞,public 参数使您无法控制域对象中值何时以及如何更改,同时完全绕过任何验证和检查。

标签: php oop model-view-controller domain-driven-design datamapper


【解决方案1】:

User.isAdmin() 一开始我就想到了。但是在您的情况下,似乎 User 和 UserGroup 一起确定了 User 是否是管理员。所以我们不能把责任放在用户或用户组。

这种情况我一般会引入一个规范对象(对不起,例子是用Java写的)。

public class AdminSpec {
   private SomeComponetToRetrieveUserGroup userGroup;

   public AdminSpec(SomeComponetToRetrieveUserGroup userGroup) {
       this.userGroup = userGroup;
   }

   public boolean isSatisfiedBy(User user) {
       // Perform the actual check
   }
}

因此,域对象不会被不希望的责任破坏,域逻辑仍然保留在域层中(而不是在应用程序服务中)。

您可以在 DDD 书籍或http://en.wikipedia.org/wiki/Specification_pattern 中找到有关规范模式的更多详细信息。

希望这会有所帮助。

【讨论】:

  • 那么 AdminSpec.isSatisfiedBy 会在注入用户组对象后从服务类中调用吗?我知道这将如何转移责任,但是为这样的检查创建类似乎有点过分,不仅仅是这个例子,而且几乎是由多个对象定义的任何属性。 (user->isAdmin 实际上是我在意识到自己的错误之前所拥有的。)
  • @birdieblue 是否添加额外的规范类完全取决于在您的应用程序中获得的优势(解耦)是否值得成本(额外的类)。
【解决方案2】:

首先,恕我直言,您在错误的级别执行授权检查(阅读this post 了解更多详细信息)。另一件事是 - 您似乎正在从模型层将数据返回给控制器(或您的区域等效)。这样的boolean 响应表明,您的控制器中有应用程序逻辑溢出。

如果你想坚持你目前的结构,那么这样的事情会更有意义:

class UserService
{
    // .. snip
    public function someMethod( $id ) 
    {

        $userMapper = $this->dataMapperFactory->build('user');
        $groupMapper = $this->dataMapperFactory->build('user');

        $user = $this->domainObjectFactory->build('user');
        $user->setId($id);
        $group = $this->domainObjectFactory->build('usergroup');
        $group->setId( UserGroup::ADMIN_GROUP );

        $userMapper->fetch($user);
        $groupMapper->fetch($group);


        return $group->hasUser( $user );
    }

}

我的 2 美分

【讨论】:

  • 有效点,它确实会流血。这证实了我的直觉 something 我的方法有问题。我以前看过您的链接帖子,但需要重新访问。
  • 我将授权部分移到正确的级别,服务类中不再有这样的功能。 但是,我可能仍需要在其他某个时间点读取用户的组成员身份(或由多个对象确定的任何其他类似属性)。我认为 Hippoom below 建议的规范对象将是可行的方法。鉴于你的两个答案,还有什么额外的 cmets 吗?
  • 嗯..有一件事。您当前的方法似乎表明,每个用户只能属于一个组。如果您 100% 确信这不会改变,请保持原样。否则,您真的可能会考虑在用户和组之间建立many-to-many 关系。
  • 实际上恰恰相反,一个用户可以是许多不同组的一部分。否则我会直接将 the 组作为用户对象的属性。但是,我确实仅以用户和组为例。我遇到的主要问题实际上是当对象的值由多个对象编译时如何定义它的属性。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-04
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多