【问题标题】:DataMapper Pattern with m:n relation (Domain Driven Design)具有 m:n 关系的 DataMapper 模式(领域驱动设计)
【发布时间】:2014-06-07 00:15:45
【问题描述】:

我目前正在根据领域驱动设计重构我的软件。问题是我对 DataMapper 部分的理解,特别是如果某些数据通过连接表具有m:n 关系(在 SQL 的情况下)。

在这种情况下,我有一个域对象:

class UserGroup extends DomainObject {
    public $title;
    public $description;    
}

现在在我的理解中,我会这样得到一个对象:

$userGroup = UserGroupMapper::instance()->findById(42);
$userGroup->title = 'Foo';
$userGroup->description = 'Bar';
UserGroupMapper::instance()->save($userGroup);

我现在走对路了吗?

那么问题来了:

我有 3 张桌子:

core_users

id | etc 

core_usergroups

id | title | description

core_usergroups_dependencys

usergroup_id | user_id

基本上,目标是找到特定组中的所有用户(或反向方式)

我想到的第一件事:

$userGroup = UserGroupMapper::instance()->findById(42);
$userCollection = UserGroupMapper::instance()->findUsersByGroup($userGroup);

DataMapper 知道 2 个表和 2 个域对象,但这感觉不对,是吗?

我不了解 DDD 的服务部分。根据它(以我的理解)我会写

UserGroupService::instance()->findUsers($userGroup);

但在这种情况下,服务需要一个自己的数据映射器。为什么我不应该打电话

UserGroupDependencyMapper::instance()->findUsersByGroup($userGroup);
UserGroupDependencyMapper::instance()->findGroupsByUser($user);

我一般不理解依赖关系的映射。 DataMapper 应该每次都返回一个对象实例或相同类型的对象实例的集合。但在这里,映射器可以返回用户或组。

那么,对于这样一个常见的问题,我应该采取什么方法呢?奖励:在这个示例中我的服务应该做什么?

更新以澄清

我的问题不在于使用领域驱动设计中的数据。它是关于如何在不获取全部的情况下获取 m:n 关系中的数据列表。界面应该是怎样的?

根据 DataMapper 模式,我基本上需要这个:

select * from core_users
join core_usergroups_dependency on core_users.id = core_usergroups_dependency.user_id
where core_usergroups_dependency.usergruop_id = 42

但是呼叫findUsersByUserGroup($myGroup) 呼叫的位置在哪里?用户映射器?

如果我有那个 Mapper 或者我们怎么称呼它,哪一部分知道它?域存储库?

【问题讨论】:

  • 您的主要问题是您说要使用 DDD,但您谈论的是表、持久性细节。 DDD忽略db,你只有repository接口
  • 当然。但我的问题不是 DDD。问题在于作为 DDD 一部分的持久层的实现。
  • @MikeSW 我的意思是,我的问题是关于存储库接口以及它的实现。
  • DDD 不关心持久性,它是关于根据领域对业务逻辑进行建模。如何实现持久化是你的事,DDD 与它无关。唯一与持久性相关的是存储库接口,它在 DDD 中始终与聚合根一起使用。在处理持久性时,一旦定义了存储库接口,DDD 就结束了。因此,您可以疯狂地执行实现,并且永远不会更改接口以适应实现。
  • @MikeSW 好的,但是如果这是我的业务逻辑,您要求我查找所有 42.000.000 个用户和 42.000 个用户组以检查业务逻辑彼此之间的关系吗?还是我理解错了?理论上有 100 条记录还可以,但在拥有数十亿条记录的现实世界中,性能会大打折扣。

标签: php service domain-driven-design relationship datamapper


【解决方案1】:

该方法是存储库接口定义的一部分(在域中定义)。 实现 是持久性的一部分并执行实际查询。域服务只知道抽象。

// in the Domain

interface IUsersRepository  // I think we need a better name
{
    public function getUsersByGroup($myGroup);
}

class MyDomainService
{
    private $repo; 

    public function __construct(IUsersRepository $repo)
    {
        $this->repo = $repo;
    }

    public function doSomething($group)
    {
        $groups = $this->repo->getUsersByGroup($group);
        // process $groups
    }
}

// in DAL

class MyRepo implements IUsersRepository
{
    // implementation
}

这里重要的是域不知道如何您获得这些组。只有实现知道这一点,所以你可以在那里做任何你想做的查询。域与查询本身分离。

【讨论】:

  • 非常感谢 :) 最后一个问题。我的假设是否正确,MyDomainService 有 2 种方法 getUserGroupById($id)getUserGroupRootAggregate($userGroup) 或者我的表示层如何告诉我,我想要 id 为 42 的组?
  • 演示文稿将使用不同的接口,并且可能会使用不同的实现,它将仅返回 UI 所需的数据,即使用 CQRS(有一个带有业务规则的域模型和另一个用于查询的简单域模型)。对于应用程序的某些部分,模型将非常简单,可以同时用于这两个目的。但是你需要记住,Domain 只关心 Domain 的需求,从不关心 UI 或其他。其他层有自己的接口,也许还有实现
猜你喜欢
  • 2011-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-08
  • 1970-01-01
  • 2015-05-17
  • 2011-10-06
相关资源
最近更新 更多