在Model层注册一个新用户.我做得对吗?


Registering a new user in the Model layer. Am I doing it right?

在过去的几天里,我一直在阅读DDD的很多内容,却找不到一个可靠的例子,说明有人会在他们的网站上简单地注册一个用户,所以在阅读了很多之后,我把这个放在一起,我想要你的反馈,因为我确信它远非完美,它甚至可能是完全错误的,但这里是:

RegisterController

$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'

谢谢。

一些与理论有关的"废话":

你可能已经意识到,MVC和MVC启发的设计模式的核心概念是SoC。它指示您将这些模式划分为主要层:表示层和域模型层。

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

你最终违反了上述SoC和SRP。

注意:在基于web的MVC变化的上下文中,"用户"是一个web浏览器,而不是坐在它后面的人。

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

不过,我还是建议你的服务范围要广一些。在用户注册的情况下,我将使其成为CommunityServiceMembershipService的一部分。就模型层而言,处理用户帐户管理的所有方面的结构。

代码位:

使用

的一种方法是:

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域对象的实例接收适当的值。实际上,数据验证是域对象的职责之一。您仍然可以使用单独的验证类,以避免代码重复,但这将是一个依赖项,由域对象的工厂注入以在实例之间共享。

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

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

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

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