为多步骤数据库操作分离服务和存储库之间的关注点

时间:2013-12-28 00:53:07

标签: php asp.net design-patterns domain-driven-design repository-pattern

在以下两个用户服务示例中考虑差异此处我使用的是PHP,但该问题适用于任何语言):

例1:

class UserService {

    public function save($id, $data)
    {
        UserRepository::save($id, $data);  // Updates user and user history, etc
    }
}

示例2:

class UserService {

    public function save($id, $data)
    {
        UserRepository::save($id, $data);        // Updates user
        UserRepository::saveHistory($id, $data);  // Updates user history
        etc...
    }
}

这是一个简单的例子,但是在项目中很难知道我们何时处理“业务问题”而不是“数据持久性问题”(如果我在这里没有使用正确的术语,我很抱歉) 。 关于使用服务和存储库的正确SoC,这两种使用存储库模式的完全可接受的方式是什么?我们怎么知道何时使用其中一个?

4 个答案:

答案 0 :(得分:1)

以上例子打破了单一责任原则。 UserRepository应该只关注用户持久性。历史通常存储在一些“历史表”中,因此存储在HistoryService中。我可以想象它需要一些额外的逻辑,而不仅仅是持久性(例如,从实体中提取已更改的数据......)

这样的HistoryService对象(我们以前称之为AuditService)将被注入到Dependency Injection Container中的Repository中。代码可能如下所示:

class UserService {

    private $historyService;

    public function __construct(HistoryService $historyService)
    {
        $this->historyService = $historyService;
    }

    public function save($id, $data)
    {
        UserRepository::save($id, $data);        // Updates user
        $this->historyService->saveHistory('user', $id, $data);  // Updates user history
        etc...
    }
}

答案 1 :(得分:0)

这与模式无关。这是责任。 save()始终是save currentsave history吗?因为如果是,第一种方式更好,但背景很重要。你应该有两个用于save和savehistory的模型方法以及对它们进行分组的逻辑存储库。

第二种变体是错误的,不是因为模式方式,而是因为缺乏检查。

我不知道savesaveHistory究竟应该是什么意思,但是假设你有users表存储用户信息,包括最后一次登录。并user_login_history保存每次登录信息。

在这种情况下,save()将尝试为用户lastlogin插入当前时间戳。但如果失败怎么办?在某些情况下,save()方法返回false。暂时缺少数据库连接,超时,错误的数据类型等。但该方法返回非真值。从UserServices::save()退出后,Repository::save()的执行将继续,然后将转到saveHistory(),并将为此用户添加具有当前时间戳的login_history,即使您的用户无法登录也是如此。所以你会有错误的数据。

如果您想使用第二种方式,请进行适当的检查。最简单的方法是:

public function save($id, $data)
{
    if(UserRepository::save($id, $data)) {        // Updates user
        UserRepository::saveHistory($id, $data);  // Updates user history
        ... // etc related method based on successful save()
    }
}

确保正确执行方法,但坚持单一责任。

答案 2 :(得分:0)

添加到我自己的问题的答案:

我认为如果服务永远不需要使用聚合的某些部分,那么存储库可以处理这些聚合部分,而不是依赖来自服务的调用来执行此操作。那么问题就变成了:“用户历史”与“用户”结合了多少。这么多,它永远不会需要一个独立的服务吗?

答案 3 :(得分:0)

当您的示例显示缺少任何“业务问题”时,您在谈论什么?在这两种情况下,它只是n层CRUD。在这两种情况下,UserService都是无意义的类,它只是将atcions重定向到另一个类。

您说“项目中很难知道我们何时处理业务问题而不是数据持久性问题”。这是一个你不需要DDD的标志。在真正需要DDD的项目中,差异是显而易见的:所有创建,读取,更新,删除和前4个同义词都是持久性问题。

在您的域中,这些业务问题可能是:用户注册了简报,用户访问了某些内容,购买了某些内容。考虑实际执行的业务任务,而不是将其映射到表的方式。