zeeloengineering/php-shared-kernel

为DDD应用提供的简单共享内核,包含许多适用于六边形架构、事件源等的脚手架代码。


README

为DDD应用提供的简单共享内核,包含许多适用于六边形架构、事件源等的脚手架代码。

实体

您可以通过从Entity类继承来创建一个新的实体。

<?php

use StraTDeS\SharedKernel\Domain\Entity;
use StraTDeS\SharedKernel\Domain\Id;

class Person extends Entity
{
    private $name;
    
    private $surname;

    public function __construct(Id $id, string $name, string $surname)
    {
        parent::__construct($id);
        
        $this->name = $name;
        $this->surname = $surname;
    }
    
    public function getName(): string
    {
        return $this->name;
    }
    
    public function getSurname(): string
    {
        return $this->surname;
    }
}

实现构造函数是强制性的,因为Id由基类控制。

只要您继承了Entity,您就有EventStreamAvailable,因此您可以记录事件并检索EventStream。

<?php

use StraTDeS\SharedKernel\Domain\Entity;
use StraTDeS\SharedKernel\Domain\Id;
use StraTDeS\SharedKernel\Domain\UUIDV4;

class Person extends Entity
{
    private $name;
    
    private $surname;

    public function __construct(Id $id, string $name, string $surname)
    {
        parent::__construct($id);
        
        $this->name = $name;
        $this->surname = $surname;
        
        $this->recordThat(
            new PersonCreated(
                UUIDV4::generate(),
                $this->getId(),
                $name,
                $surname
            )
        );
    }
    
    public function getName(): string
    {
        return $this->name;
    }
    
    public function getSurname(): string
    {
        return $this->surname;
    }
}

$person = new Person(
    UUIDV4::generate(),
    'Alex',
    'Hernández'
);

$eventStream = $person->pullEventStream();

实体集合

由于您最终将从存储库返回实体的数组,我已经为您准备了一个简单的实体集合,您可以直接继承。

<?php

use StraTDeS\SharedKernel\Domain\EntityCollection;
use StraTDeS\SharedKernel\Domain\UUIDV4;

class PersonCollection extends EntityCollection
{
    
}

$personCollection = new PersonCollection([
    new Person(
        UUIDV4::generate(),
        'Alex',
        'Hernández'
    ),
    new Person(
        UUIDV4::generate(),
        'John',
        'Smith'
    )
]);

$entities = $personCollection->getEntities();

领域事件

当您想要创建一个事件时,您可以从一个基类DomainEvent继承。就是这样简单。

<?php

use StraTDeS\SharedKernel\Domain\DomainEvent;
use StraTDeS\SharedKernel\Domain\Id;

class PersonCreated extends DomainEvent
{
    private $name;
    
    private $surname;
    
    public function __construct(Id $id, Id $entityId, string $name, string $surname)
    {
        parent::__construct($id, $entityId);
        
        $this->name = $name;
        $this->surname = $surname;
    }
    
    public function getName(): string
    {
        return $this->name;
    }
    
    public function getSurname(): string
    {
        return $this->surname;
    }
}

用例

通过Application UseCase,您可以从控制台命令或控制器中非常简单地访问领域和基础设施层。一个UseCase接收一个请求,并可能返回(或null)一个响应。让我们看看一个例子。

<?php

use StraTDeS\SharedKernel\Application\UseCase\UseCase;
use StraTDeS\SharedKernel\Application\UseCase\Request;
use StraTDeS\SharedKernel\Application\UseCase\Response;
use StraTDeS\SharedKernel\Domain\Id;
use StraTDeS\SharedKernel\Application\DataTransformer;

class PersonCollectionToArrayDataTransformer implements DataTransformer
{
    /**
     * @param mixed|PersonCollection $data
     * @return array
     */
    public function transform(mixed $data): mixed
    {
        $persons = [];
        
        foreach($data as $person) {
            $persons[] = $person->toArray();
        }
        
        return $persons;
    }
}

class GetUserByIdRequest extends Request
{
    private $id;
    
    public function __construct(Id $id) 
    {
        $this->id = $id;
    }
    
    public function getId(): Id
    {
        return $this->id;
    }
}

class GetUserByIdResponse extends Response
{
    private $persons;
    
    public function __construct(mixed $persons) 
    {
        $this->persons = $persons;
    }
    
    public function getPersons(): mixed
    {
        return $this->persons;
    }
}

class GetUserByIdUseCase extends UseCase
{
    private $dataTransformer;
    
    public function __construct(DataTransformer $dataTransformer) 
    {
        $this->dataTransformer = $dataTransformer;
    }
    
    public function execute(Request $getUserByIdRequest): Response
    {
        $userCollection = //my repository query returns a PersonCollection
        
        return new GetUserByIdResponse($this->dataTransformer->transform($userCollection));
    }
}

您可能已经注意到了DataTransformer对象。它提供了将任何对象转换成应用程序层中任何其他对象的功能。这意味着您可以通过注入不同的数据转换器来使用相同的UseCase以不同的格式检索信息。这是基本的不将领域对象返回给基础设施层的方法。

我建议根据注入的数据转换器定义不同的用例名称。例如

  • get_user_by_id_use_case_data_transformed_to_array
  • get_user_by_id_use_case_data_transformed_to_json

如果您使用了一个不错的依赖注入器,这可以很容易地完成。

CQRS

CQRS代表命令查询责任分离,基本上意味着一个方法应该要么返回一个值,要么修改其上下文,但不要同时做这两件事。

我提供了一些有用的接口,既可以用于命令(以更改上下文),也可以用于查询(以检索信息)。这通常通过命令和查询总线来完成。总线接受某种类型的请求(一个命令或一个查询)并通过处理器(一个CommandHandler或一个QueryHandler)处理它。可以在处理过程中添加中间件。

因此,在namespace StraTDeS\SharedKernel\Application\CQRS中有命令和查询的抽象类,以及命令处理器和查询处理器的接口。

存储库

有一些有用的接口可以实现存储库,包括get、find、all接口和save接口。此外,我还包括两个基本的Doctrine存储库,DoctrineRepository和DoctrinePersistentRepository。您可以使用它来免费获得一些功能。

<?php

use StraTDeS\SharedKernel\Infrastructure\DoctrinePersistentRepository;
use StraTDeS\SharedKernel\Domain\UUIDV4;

class DoctrinePersonRepository extends DoctrinePersistentRepository implements PersonRepository
{
    public function getEntityName(): string
    {
        return Person::class;
    }
}

// person repository creation ...

$person = $personRepository->get(UUIDV4::fromString('6238ec41-71d0-4482-97f5-4c5c4919e635'));
$person->changeName('John');
$personRepository->save($person);