谁应该处理复杂查询,数据映射器或服务层中的条件?

oro*_*kek 17 php domain-driven-design datamapper

这个问题在清除我对此事的困惑方面做得非常好,但我很难找到关于服务层的确切限制应该是什么的可靠来源.

对于这个例子,假设我们正在处理书籍,我们希望通过作者获得书籍.本BookDataMapper可以有一个通用的get()接受条件(S),如书的唯一标识符,作者姓名等这个实现是相当微不足道的(逻辑)方法,但如果我们希望有需要更复杂的查询多个条件是什么?

让我们说我们希望得到某位作者在特定出版商下撰写的所有书籍.我们可以扩展BookDataMapper->get()方法来解析多个条件,或者我们可以编写一个新的方法,如BookDataMapper->getByAuthorAndPublisher().

是否最好让服务层直接调用这些[更具体]的方法,或者在调用BookDataMapper->get()多个条件传递的更通用方法之前解析条件?在后一种情况下,服务层将执行更多逻辑"繁重的工作",使数据映射器相当简单.前一个选项会将服务层几乎完全缩减为中间人,将条件逻辑留给数据映射器等方法BookDataMapper->getByAuthorAndPublisher().

让服务层解析条件的一个明显问题是某些域逻辑泄漏出数据映射器.(这在此处的链接问题中进行了解释.但是,如果服务层要处理条件,则逻辑不会使其脱离模型层;控制器将无论如何调用$book_service->getByAuthorAndPublisher().

ter*_*ško 24

数据映射器模式只告诉你,什么是应该做的,不是它应该如何实现.
因此,本主题中的所有答案都应视为主观,因为它们反映了每位作者的个人偏好.

通常尽量保持mapper的界面尽可能简单:

  • fetch(),检索域对象或集合中的数据,
  • save(),保存(更新现有或插入新的)域对象或集合
  • remove(),从存储介质中删除域对象或集合

我将条件保存在域对象本身中:

$user = new User;
$user->setName( 'Jedediah' );

$mapper = new UserMapper;
$mapper->fetch( $user );

if ( $user->getFlags() > 5  )
{
    $user->setStatus( User::STATUS_LOCKED );
}

$mapper->save( $user );
Run Code Online (Sandbox Code Playgroud)

这样,您可以有多个检索条件,同时保持界面清洁.

这样做的缺点是你需要一个公共方法来从域对象中检索信息以获得这样的fetch()方法,但无论如何你都需要它来执行save().

没有真正的方法来实现映射器和域对象交互的"Tell Do not Ask"经验法则.

至于"如何确保你真的需要保存域对象?" ,这可能会出现在这里,它已经在这里介绍了广泛的代码示例和注释中的一些有用的部分.

更新

我的情况是,如果你希望处理对象组,你应该处理不同的结构,而不是简单的域对象.

$category = new Category;
$category->setTitle( 'privacy' );

$list = new ArticleCollection;

$list->setCondition( $category );
$list->setDateRange( mktime( 0, 0, 0, 12, 9, 2001) );
// it would make sense, if unset second value for range of dates 
// would default to NOW() in mapper

$mapper = new ArticleCollectionMapper;
$mapper->fetch( $list );

foreach ( $list as $article )
{
    $article->setFlag( Article::STATUS_REMOVED );
}

$mapper->store( $list );
Run Code Online (Sandbox Code Playgroud)

在这种情况下,集合是glorified数组,能够接受不同的参数,然后用作映射器的条件.当映射器试图存储集合时,它还应该让映射器从该集合中获取列表更改的域对象.

在这种情况下,映射器应该能够构建(或使用预设的)具有所有可能条件的查询(作为开发人员,您将了解所有这些条件,因此您不需要使其在无限条件下工作)和更新或创建该集合包含的所有未保存域对象的新条目.

注意:在某些方面,您可以说,映射器与构建器/工厂模式相关.目标不同,但解决问题的方法非常相似.


hak*_*kre 5

我通常更喜欢这样更具体,例如:

BookDataMapper->getByAuthorAndPublisher($author, $publisher)
Run Code Online (Sandbox Code Playgroud)

那是因为我不需要重新发明 SQL。数据库对此更好,数据映射器在这里负责,应用程序的其余部分也不需要了解有关事物如何具体存储或查询的任何信息。

如果你让它更加动态,你很容易倾向于通过界面提供太多的功能。不好。

并查看您的应用程序。您会看到,不会有太多不同的查询。对于数据的主要部分,如果有的话,通常大约有 5-10 个例程。它的编写速度比考虑一些实际上属于它自己的层的动态系统要快得多。