1

在过去的几天里,我一直在阅读关于 DDD 的很多内容,但找不到一个可靠的例子来说明某人如何在他们的网站上简单地注册用户,所以经过大量阅读后,我把它放在一起,我希望得到你的反馈因为我确信它远非完美,它甚至可能是完全错误的,但它是这样的:

注册控制器

$userMapper = $this->dataMapperFactory->build('user');
if($userMapper->fetchByUsername($username) !== NULL) {
    // Error: The chosen username already exists
}
else {

    if($userMapper->fetchByEmail($email) !== NULL) {
        // Error: The email address already exists
    }
    else {

        $userDO = $this->domainObjectFactory->build('user');
        // Set the properties of the $userDO object here with the ones
        // from the registration form

        // Insert the new user into the database            
        $userMapper->save($userDO);

    }

}

我已经用自己的FormValidation类完成了所有表单验证,所以当我将属性添加到 $userDO 对象时,它们都 100% 准备好插入数据库(正确的长度、类型、格式、范围等),那么代码如何看你?

我认为我走在正确的轨道上,我非常感谢有关如何改进我的代码的任何提示。

另外,我检查他们选择的用户名是否已经被使用的方式,有没有更好的方法来做到这一点?而不是每次都创建一个对象来检查?就像我以前用简单的方法做的那样:

SELECt COUNT(*) FROM users WHERE username = 'john'

谢谢。

4

1 回答 1

3

一些与理论相关的“废话”:

您可能知道,MVC 和受 MVC 启发的设计模式的核心概念是SoC。它要求您将这些模式划分为主要层:表示层和域模型层。

在这种情况下,它很重要,因为您当前的控制器结构包含应用程序逻辑(交互域逻辑实体和存储抽象),而控制器应该只负责更改模型层的状态(有时 - 当前视图)基于用户输入。

你最终违反了上面提到的 SoC 和SRP

注意:在基于 Web 的 MVC 变体的上下文中,“用户”是 Web 浏览器,而不是坐在它后面的人。

相反,您应该将应用程序逻辑封装在服务中(如@Gordon所述)。在完全实现的模型层中,不同的服务变成了类似于公共API 的东西,表示层通过它与模型交互。

不过,取消 Gordon 的链接,我建议您的服务范围更广一些。CommunityService在用户注册的情况下,我会将其作为MembershipService. 就模型层而言,处理用户帐户管理的所有方面的结构。

代码位:

在控制器中使用的一种方法如下所示:

public function postUser( $request )
{
    $community = $this->serviceFactory->build('Community');
    $community->addUser( $request->getParameter('username'),
                         $request->getParameter('password'),
                         $request->getParameter('repeated_password'),
                         $request->getParameter('email') );
}

虽然这是一种有效的方法,但您可能已经注意到一个可能的问题。即使用户注册只需要最少的数据,您最终传递给服务的参数量也会使其难以使用。

将其传递$request给服务并不是有效的改进。你最终会违反得墨忒耳法则。相反,我会推荐类似的东西:

$keys = ['username', 'password', 'repeated_password', 'email'];
$community->addUser( $request->getParameters( $keys ) );

getParameters()方法的实现类似于:

public function getParameters( $keys )
{
    $response = [];
    foreach( $keys as $parameter )
    {
        $response[ $parameter ] = $this->getParameter( $parameter );
    }
    return $response;
}

领域逻辑和验证

您提到,FormValidation您用来确保您的User 域对象实例接收正确值的某个类。实际上,数据验证是域对象的职责之一。您仍然可以使用单独的验证类来避免代码重复,但这将是一个依赖项,它由域对象的工厂注入以在实例之间共享。

注意:根据我的个人经验,除了空检查之外,验证的重复很少见。每个复杂的验证规则集都针对一个特定域对象的字段。在我看来,这使得验证类变得非常多余……除非您希望在多个项目之间共享相同的验证类。

代码流通常是这样的,当您需要存储来自域对象的数据时,您检查它是否没有获取错误状态,如果有错误,您实际上将其转储到会话中,以便在重定向后检索.

if ( $user->isValid() )
{
    $sqlMapper->store( $user );
}
else
{
    $sessionMapper->storeUser();
}

在这个用例中,预先验证的输入最终实际上是有害的。

于 2013-01-05T21:07:16.610 回答