DDD中的服务/存储库设计

时间:2018-04-05 20:36:55

标签: php domain-driven-design repository-pattern

我目前正在学习DDD,出于测试目的,我正在开发一个简单的博客系统,但我无法弄清楚如何正确设计我的服务和存储库。

我有:

  • 发布实体
  • 发表评论实体
  • 发布标签实体

我有一个页面,我想显示一个特定帖子的所有评论和标签。

我开始使用以下控制器操作:

function showPost($postId) {
    try {
        $postEntity = $this->postService->find($postId);
    } catch {PostNotFoundException $e) {
        // ...
    }

    $postComments = $this->postCommentService->findAll($postEntity);
    $postTags     = $this->postTagService->findAll($postEntity);

    // return to view...  
}

在互联网上提供的大多数示例中,我都知道直接与控制器(应用层)中的实体合作并不是一个好主意,因此只能在域服务中使用entites。所以我试着把它们移到那里:

function showPost($postId) {
    $postComments = $this->postCommentService->findAll($postId);
    $postTags     = $this->postTagService->findAll($postId);

    // return to view...  
}

但是现在我的服务中有重复的代码,我必须在两个服务中注入PostRepository:

// in postCommentService
function findAll($postId) {
    try {
        $postEntity = $this->postRepository->find($postId);
    } catch (PostNotFoundException $e) {
        // ...
    }
    $postComments = $this->postCommentRepository->findAll($postEntity);
    return $postComments; // convert to DTO before return
}

// in postTagService
function findAll($postId) {
    try {
        $postEntity = $this->postRepository->find($postId);
    } catch (PostNotFoundException $e) {
        // ...
    }
    $postTags = $this->postTagRepository->findAll($postEntity);
    return $postTags; // convert to DTO before return
}

第三个问题是我在视图中也需要postEntity(或其DTO等价物),所以在控制器中会有另一个(第三个)查询。

我的问题是否有通用的解决方案?根据id(postId)及其“子对象”(注释和标签)查询对象(post)?

或者可以在控制器中查询实体并直接用它来查询服务吗?

1 个答案:

答案 0 :(得分:1)

  

我的问题是否有通用解决方案?

没有魔力。

你可能应该考虑“观看服务”;将postId作为参数的东西,并返回一个DTO,其中包含构建视图所需的内容。

视图服务依次需要三种方法

  • postId - >交
  • postId - >列表(评论)
  • postId - >列表(标签)

关键的想法是,一旦你有了帖子,评论列表和标签列表,你实际上并不关心它们来自哪里。这就是存储库的重点 - 它hides the decision关于如何从不关心的代码部分持久保存数据。

try {
    $postEntity = $this->postRepository->find($postId);
    $postComments = $this->postCommentRepository->findAll($postId);
    $postTags = $this->postTagRepository->findAll($postId);

    return $this->createDTO($postEntity, $postComments, $postTags);
} catch (PostNotFoundException $e) {
    // ...
}

对于仅从数据模型中读取的用例,一旦获得了所需的原始数据,就不需要再考虑存储库了。在与存储库交互的代码与正在对实体的内存表示进行工作的代码之间进行明确的逻辑分离通常很有用。

如果你想要一个更“面向对象”的API,你可以把构建DTO的工作放到post实体本身

try {
    $postEntity = $this->postRepository->find($postId);
    return $postEntity->createDTO($this->postCommentRepository, $this->postTagRepository);

} catch (PostNotFoundException $e) {
    // ...
}

这里,存储库作为一种“域服务”而存在,为postEntity提供了构建DTO所需的读​​取功能。