实现三结构模型(领域对象、数据映射器和服务)时存在一些不确定性


Some uncertainty with implementing the 3 structure Model (Domain object, data mapper and service)

正如标题所示,我在实现3结构模型(领域对象,数据映射器和服务)时遇到了一些小问题。

过去,当有人在我的网站上注册时,我只会输入

$user->register($firstName, $lastName, $emailAddress, $username...);

,该方法将按如下步骤运行

1. Check if the form sent was valid.
2. Check if all the required fields were filled.
3. Check the if the lengths of strings were valid and the range of integers etc.
4. Check if the input is in the correct format (regex).
5. Check if the username is already taken and if the email address already exists
   in the database
6. etc. etc.

所有的工作都很好,但我试图避免这样做,因为我希望我的代码更可重用和可测试。

现在,有了这个3结构模型,领域对象和数据映射器应该通过一个服务进行通信,以保持它们彼此隔离,所以这是我对用户服务的想法

class UserService {
    public function register($firstName, $lastName, $email...) {
        $userDO= $this->domainObjectFactory->build('User');
        $mapper  = $this->dataMapperFactory->build('User');
        // Is this where I start doing my validation like in the steps above???
        // And if this is where I start doing my checks, when I get to the part
        // where I have to check if the username they want is already taken how
        // how do I do that check?
    }    
}

然后要运行它,我可以在控制器中输入如下

$userService = $this->serviceFactory->get('user');
$result = $userService->register($_POST['firstName']....);

逻辑(if's和else's)必须在我的UserService类的register()方法中,对吗?因为如果它们进入域对象当我需要数据库做一些检查的时候比如用户名是否已经存在我该如何访问数据库?我真的不知道,因为域对象不应该知道关于数据源的任何事情。

必须有一种方法来访问数据库的小查询,如检查用户名或电子邮件地址是否已经存在,以及其他需要完成的小查询负载。

我有很多实体/领域对象需要做小查询的负载,在过去,我的模型可以从任何方法内访问数据库,可以做这些查询,但这似乎不允许与这个3结构模型,我很想知道什么是正确的方法去做,因为必须有一种方法。

我一直在飞行,直到我发现一个模型是一个被分成3个结构的层。

任何帮助或推动在正确的方向将非常感激,特别是好的现实生活中的例子。对于我的具体问题,互联网似乎缺乏这些。

谢谢。

我正在经历与您现在相同的事情(有趣的是,对于这个确切的问题-用户注册/授权)。

我的一个建议是,你不应该限制你的模型只有3层(在这种情况下,3个类)。为了在保持SRP(单一职责原则)完整性的同时完成工作,模型应该包含尽可能多的类。

例如,您可能还希望使用UserTableGateway类来补充UserDataMapper,并使用UserCollection类来允许潜在的分页功能或一般用户列表。所有这些东西都可以是你的模型和UserService层的一部分。

要回答你关于特定于注册过程的逻辑的问题,是的,它最合适的位置是在UserService类的注册方法中。

说了这么多,你可能要考虑一下你的域结构。UserService是最适合注册的地方(并通过扩展,登录,恢复密码,更改密码等)?

也许这些东西可以是与account相关的完全不同的模型的一部分。服务类可以是这样的:

class Account 
{
    // Think of this as AccountService, it will have a number of collaborators,
    // including an Authenticator class for loggin in, a Session class for managing the session
    // and of course various persistence classes for storing the user, session and credentials

    public function register(UserInterface $user) {}
    public function login($email, $password) {}
    public function logout(UserInterface $user, $session_id) {}
    public function changePassword($user_id, $old_password, $new_password) {}
    public function recoverPassword($user_id, $email) {}
}

考虑到UserService可能负责许多其他事情,将与用户帐户相关的所有内容保留在其自己的服务类中是有意义的。

从概念上讲,帐户与用户是不同的,如果它们在概念上不同,就应该有自己的类。

现在,就获取Account服务类所需的依赖关系而言,在我对复杂系统设计的理解中,这超出了我的范围。

这是个有趣的问题。我想你的用户数据映射器有方法来查找&根据某些标准(如用户名)加载用户。因此,在这种情况下,我将在UserService::register中进行检查,尝试查找具有提供的用户名(如果存在)的用户:


if ($mapper->findUserByUsername($username)) { 
    // username is already taken, handle this special case
}

顺便说一下,我不知道你是否读过这篇文章,Martin的Fowler的书——企业应用程序架构模式是一个很好的指南。你会找到很多问题的好答案。