PostService   A
last analyzed

Complexity

Total Complexity 4

Size/Duplication

Total Lines 72
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
wmc 4
lcom 1
cbo 5
dl 0
loc 72
rs 10
c 0
b 0
f 0

3 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A create() 0 17 2
A delete() 0 4 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Explicit Architecture POC,
7
 * which is created on top of the Symfony Demo application.
8
 *
9
 * (c) Herberto Graça <[email protected]>
10
 *
11
 * For the full copyright and license information, please view the LICENSE
12
 * file that was distributed with this source code.
13
 */
14
15
namespace Acme\App\Core\Component\Blog\Application\Service;
16
17
use Acme\App\Core\Component\Blog\Application\Query\FindHighestPostSlugSuffixQueryInterface;
18
use Acme\App\Core\Component\Blog\Application\Query\PostSlugExistsQueryInterface;
19
use Acme\App\Core\Component\Blog\Application\Repository\PostRepositoryInterface;
20
use Acme\App\Core\Component\Blog\Domain\Post\Post;
21
use Acme\App\Core\Port\Lock\LockManagerInterface;
22
use Acme\App\Core\SharedKernel\Component\User\Domain\User\UserId;
23
24
final class PostService
25
{
26
    public const SLUG_LOCK_PREFIX = 'slug_creation-';
27
28
    /**
29
     * @var PostRepositoryInterface
30
     */
31
    private $postRepository;
32
33
    /**
34
     * @var PostSlugExistsQueryInterface
35
     */
36
    private $postSlugExistsQuery;
37
38
    /**
39
     * @var FindHighestPostSlugSuffixQueryInterface
40
     */
41
    private $findHighestPostSlugSuffixQuery;
42
43
    /**
44
     * @var LockManagerInterface
45
     */
46
    private $lockManager;
47
48
    public function __construct(
49
        PostRepositoryInterface $postRepository,
50
        PostSlugExistsQueryInterface $postSlugExistsQuery,
51
        FindHighestPostSlugSuffixQueryInterface $findHighestPostSlugSuffixQuery,
52
        LockManagerInterface $lockManager
53
    ) {
54
        $this->postRepository = $postRepository;
55
        $this->postSlugExistsQuery = $postSlugExistsQuery;
56
        $this->findHighestPostSlugSuffixQuery = $findHighestPostSlugSuffixQuery;
57
        $this->lockManager = $lockManager;
58
    }
59
60
    public function create(Post $post, UserId $authorId): void
61
    {
62
        $post->setAuthorId($authorId);
63
64
        // We acquire a lock on the creation of a slug here, to prevent race conditions while generating the sequential
65
        //  ID used to make the slug unique.
66
        // We use the slug as part of the lock name so that we only block the post creation requests that try to
67
        //  create a post with the same slug.
68
        $this->lockManager->acquire(self::SLUG_LOCK_PREFIX . $post->getSlug());
69
70
        if ($this->postSlugExistsQuery->execute($post->getSlug())) {
71
            $highestPostSlugSuffix = $this->findHighestPostSlugSuffixQuery->execute($post->getSlug());
72
            $post->postfixSlug((string) ++$highestPostSlugSuffix);
73
        }
74
75
        $this->postRepository->add($post);
76
    }
77
78
    /**
79
     * This method is quite simple, so it might seem useless to have it. However, it is debatable if we should have it
80
     * or not, and several things come to my mind:
81
     *  On one hand, this use case can be triggered from several locations and if/when we need to change it
82
     *      (ie to make it a soft delete instead of a hard delete or to send out an event), we would only need to
83
     *      change it here.
84
     *  On the other hand, being so simple, we could just use the repository directly in the controllers,
85
     *      and move the logic to a service only if/when the logic becomes more complex.
86
     *  Yet on another hand, if we use a command bus, any data changes must be done through a command/handler
87
     *      (so they can be queued and possibly retried) and such a simple operation is no exception.
88
     *
89
     * For the sake of the previous explanation, I decided to leave it here.
90
     */
91
    public function delete(Post $post): void
92
    {
93
        $this->postRepository->remove($post);
94
    }
95
}
96