在过去的几天里,我一直在阅读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。
相反,您应该将应用程序逻辑封装在服务中(如@Gordon所提到的)。在完全实现的模型层中,不同的服务变成了类似于公共的 API的东西,表示层通过它与模型交互。注意:在基于web的MVC变化的上下文中,"用户"是一个web浏览器,而不是坐在它后面的人。
不过,我还是建议你的服务范围要广一些。在用户注册的情况下,我将使其成为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
域对象的实例接收适当的值。实际上,数据验证是域对象的职责之一。您仍然可以使用单独的验证类,以避免代码重复,但这将是一个依赖项,由域对象的工厂注入以在实例之间共享。
注意:根据我的个人经验,除了null检查之外,验证的重复是相当罕见的。每个复杂的验证规则集都针对一个特定域对象的字段。在我看来,这使得验证类相当多余……除非您希望在多个项目之间共享相同的验证类。
代码流通常是这样的,当您需要存储来自域对象的数据时,您检查它是否没有获得错误状态,如果有错误,您实际上将其转储到会话中,以便在重定向后检索。
if ( $user->isValid() )
{
$sqlMapper->store( $user );
}
else
{
$sessionMapper->storeUser();
}
在这个用例中,预先验证的输入实际上是有害的。