昨天我问了一个关于单数/复数模型命名约定的问题,我遇到了域模型的概念。据我所知,到目前为止,域模型只是一个表示我的应用程序中的实体的对象。
使用一个非常简单的例子,如下所示:
class User {
private $name;
public setName() {}
public getName() {}
}
现在,我想到了一个问题,我如何从一些输入源,从数据库或数据源填充这个“域模型”?
在阅读有关域模型的过程中,我得到的印象是,在它们内部应该只提供有关域概念的表示。所以现在我还需要另一个负责与数据源交互的类(DAO?),在这种情况下是一个名为“User”的数据库表。我的DAO类将处理插入,更新,删除和多次提取。
我提出了这种方法,从输入填充用户域模型,在这种情况下发布数据,然后使用UserDAO类将记录保存到数据库:
/**
* Populating Domain Model from input, and saving to database
*/
/** Instantiate User Domain Model, and populate it from input */
$user = new User();
$user->setName($_POST['name']);
/** Creating a new database record from User Domain Model */
$userdao = new UserDAO($pdo);
$userdao->insert($user);
以下是我在需要获取数据时预期与数据库交互的方式,在这种情况下是多个用户记录:
/**
* Fetching data from database to populate User Domain Models in an array
*/
/** Instantiate new UserDAO object to interact with User table */
$users = new UserDAO($pdo);
$users->findAll();
$user_collection = [];
/** Loop UserDAO object set to populate user collection array */
foreach ($users as $user) {
/** Create new User domain object from row, and add to collection array */
$user = new User($user);
$user_collection[$user->name()] = $user;
}
这似乎唯一真正的好处是组织。
我当前的迭代本质上有一个User类,它承担上面UserDAO类的所有职责,并直接从数据库中返回数据数组,然后我在“Controllers”/“Presenters”中使用它们通过我的(被动)观点。
我想知道的是:
我是否走在正确的轨道上?
输入验证属于哪里?如果到目前为止我的假设是正确的,我认为必须使用域模型吗?
除了帮助组织应用程序依赖和操作的基本概念之外,使用此技术有什么好处?为什么我需要这个额外的层而不是直接在DB的数组结果上操作?
答案 0 :(得分:25)
这里的关键是要查看哪些类应该具有哪些职责,具体而言,需要哪些职责才能使您的域运行。
用户域对象
应负责告诉您有关应用程序有用业务规则的状态(isAdmin()
,isBanned()
,isSuspended()
,getWarnLevel()
)。将此对象视为API存储桶。你想知道什么?它可以告诉什么有用的信息?构建一个能够回答这些问题的API。小心不要让用户过多地告诉你系统中的其他对象。这不应该是它的责任。
关心:
不关心
用户存储库
一个类,负责允许您检索并保留完整形成的现有Users
。也许它们在当前请求期间仅存储在内存中。也许他们坚持缓存。也许他们坚持使用MySQL。这没关系。所有这一切都可以让你获得用户并坚持下去。这是它的责任。它可以但不必了解持久性机制。它只需要知道 如何使用 持久性机制。
(findById($id)
,findByEmail($email)
,findBanned()
,findByCriteria($Criteria)
- 策略或规范模式的合适人选,save($User)
,delete($User)
)。同样,这里的关键是构建一个满足域业务规则的API。您是否需要通过电子邮件地址查找用户?然后在存储库中创建一个显式访问点。如果你没有需要,那就不要了。你如何找到Users
?用API回答。
关心
不关心
User
个对象User
个对象用户工厂
UserRepositories
用于处理现有的User
个对象,但您如何首先创建它们?有工厂。这些可以是简单的工厂,只需要一种类型的用户。或者它们可以是抽象工厂,可以生成不同类型的用户。这取决于您和您的系统需求。同样,请考虑满足域空间业务规则所需的API:(make($type, $data)
,makeAdmin($data)
,makeMember($data)
)。 PHP 5.6的可变参数运算符语法将使这个WAY清理器与btw一起工作。
关心
Users
不关心
Users
User
制作完用户网关/映射器
这可能是您的实际持久性机制:它可能直接与关系数据库接口,使用Factory,并被存储库使用。它执行实际的DB提取,并将数据映射到Factory可以消化的格式。工厂不应该对这种映射负责,因为它不应该知道源格式,只知道组装域对象所需的格式。因此,映射责任在于Gateway / Mapper。
关心
User
持续存储/检索的位置以及不关心
User
对象现在,诚然,这看起来比实际更简单。你如何处理聚合的孩子(例如属于一个Comments
的许多Post
)?你在什么阶段给那些人Post
?你是否一直给他们,或者只有当你明确要求它时(例如通过回调?)这些是我没有得到答案的难题,而且你的域名部分回答了首先需要。
ORM是一个难以解决的问题。主义和雄辩是优秀的,但不要严格遵循上述模式。那没关系。上面的模式是一个指导原则,而不是规则。它的目标是专注于分离关注点和集中责任。您的应用程序可能没有必要拥有所有这些层。
关于验证,有两种验证:
表单输入验证
域对象验证
表单验证通常最好通过表单验证器类和为给定表单专门定义的一些规则来完成。通常,您可以在任何表单构建器类中定义这些规则(例如Form::text('first_name', array('rules' => 'required|alpha'))
)。控制器应该将表单验证器作为依赖项,但不应该自己进行实际验证。
域对象程序集验证(例如,保护模型的完整性)可以通过setter在域对象本身中存在,也可以在工厂中存在。这完全取决于您计划构建域对象的方式。
应该注意的是,您应该具有两种类型的验证:表单验证以验证输入,以及域对象验证以在创建对象时验证数据完整性。