refactor(ddd): introduce AggregateRoot and ReadModel interfaces extending Entity
This commit is contained in:
parent
e10bd7741d
commit
cb97e6f591
13 changed files with 93 additions and 33 deletions
|
|
@ -8,14 +8,14 @@ use Application\Security\Models\ValueObjects\ClientIdentifier;
|
|||
use Application\Security\Models\ValueObjects\ClientName;
|
||||
use Application\Security\Models\ValueObjects\ClientSecret;
|
||||
use Application\Security\Models\ValueObjects\Entities\ClientId;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\AggregateRoot;
|
||||
use Shared\DDD\Model\HasTimestampIdentifier;
|
||||
use Shared\Time\Utc;
|
||||
|
||||
/**
|
||||
* @implements Entity<ClientId>
|
||||
* @implements AggregateRoot<ClientId>
|
||||
*/
|
||||
final class Client implements Entity, HasTimestampIdentifier
|
||||
final class Client implements AggregateRoot, HasTimestampIdentifier
|
||||
{
|
||||
private function __construct(
|
||||
public readonly ClientId $id,
|
||||
|
|
|
|||
|
|
@ -7,13 +7,13 @@ namespace Application\Security\Models\Entities;
|
|||
use Application\Security\Models\ValueObjects\Entities\SecurityAuditLogDetails;
|
||||
use Application\Security\Models\ValueObjects\Entities\SecurityAuditLogEntryId;
|
||||
use Ramsey\Uuid\UuidInterface;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\HasTimestampIdentifier;
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
|
||||
/**
|
||||
* @implements Entity<SecurityAuditLogEntryId>
|
||||
* @implements ReadModel<SecurityAuditLogEntryId>
|
||||
*/
|
||||
final readonly class SecurityAuditLogEntry implements Entity, HasTimestampIdentifier
|
||||
final readonly class SecurityAuditLogEntry implements HasTimestampIdentifier, ReadModel
|
||||
{
|
||||
public function __construct(
|
||||
public SecurityAuditLogEntryId $id,
|
||||
|
|
|
|||
|
|
@ -9,21 +9,18 @@ use Domain\Models\ValueObjects\Brand\BrandCode;
|
|||
use Domain\Models\ValueObjects\Brand\BrandId;
|
||||
use Domain\Models\ValueObjects\Brand\BrandName;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\AggregateRoot;
|
||||
use Shared\DDD\Model\HasTimestampIdentifier;
|
||||
|
||||
/** @implements Entity<BrandId> */
|
||||
final readonly class Brand implements Entity, HasTimestampIdentifier
|
||||
/** @implements AggregateRoot<BrandId> */
|
||||
final readonly class Brand implements AggregateRoot, HasTimestampIdentifier
|
||||
{
|
||||
public BrandId $id;
|
||||
|
||||
private function __construct(
|
||||
BrandId $id,
|
||||
public BrandId $id,
|
||||
public BrandName $name,
|
||||
public BrandCode $code,
|
||||
public ArcadiaId $arcadiaId,
|
||||
) {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use Domain\Models\ValueObjects\Employees\ArcadiaEmployeeId;
|
|||
use Domain\Models\ValueObjects\EstablishmentCode;
|
||||
use Domain\Models\ValueObjects\ServiceId;
|
||||
use Domain\Models\ValueObjects\SiteId;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
|
||||
/** @implements Entity<ArcadiaEmployeeId> */
|
||||
final readonly class Employee implements Entity
|
||||
/** @implements ReadModel<ArcadiaEmployeeId> */
|
||||
final readonly class Employee implements ReadModel
|
||||
{
|
||||
private function __construct(
|
||||
public ArcadiaEmployeeId $publicEmployeeNumber,
|
||||
|
|
|
|||
|
|
@ -21,10 +21,10 @@ use Domain\Models\ValueObjects\Inco\Composition\ReferenceMacronutrients;
|
|||
use Domain\Models\ValueObjects\Inco\Composition\SpecialCompounds;
|
||||
use Domain\Models\ValueObjects\Inco\Composition\Vitamins;
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
|
||||
/** @implements Entity<IncoCompositionId> */
|
||||
final readonly class IncoComposition implements Entity
|
||||
/** @implements ReadModel<IncoCompositionId> */
|
||||
final readonly class IncoComposition implements ReadModel
|
||||
{
|
||||
private function __construct(
|
||||
public IncoCompositionId $id,
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ use Domain\Models\ValueObjects\Inco\Ingredients;
|
|||
use Domain\Models\ValueObjects\Inco\Nutriscore;
|
||||
use Domain\Models\ValueObjects\Inco\PortionAmount;
|
||||
use Domain\Models\ValueObjects\Inco\StorageCondition;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
|
||||
/** @implements Entity<IncoId> */
|
||||
final readonly class IncoSheet implements Entity
|
||||
/** @implements ReadModel<IncoId> */
|
||||
final readonly class IncoSheet implements ReadModel
|
||||
{
|
||||
/**
|
||||
* @param array<\Domain\Models\ValueObjects\Inco\Allergen> $allergens
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||
namespace Domain\Models\Entities\Inco;
|
||||
|
||||
use Domain\Models\ValueObjects\Inco\IncoId;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
|
||||
/** @implements Entity<IncoId> */
|
||||
final readonly class IncoShortSheet implements Entity
|
||||
/** @implements ReadModel<IncoId> */
|
||||
final readonly class IncoShortSheet implements ReadModel
|
||||
{
|
||||
private function __construct(
|
||||
public IncoId $id,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,20 @@ declare(strict_types=1);
|
|||
|
||||
namespace Domain\Models\ValueObjects\Brand;
|
||||
|
||||
final readonly class ArcadiaBrand
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
|
||||
/** @implements ReadModel<ArcadiaId> */
|
||||
final readonly class ArcadiaBrand implements ReadModel
|
||||
{
|
||||
public function __construct(
|
||||
public ArcadiaId $id,
|
||||
public BrandName $name,
|
||||
) {
|
||||
}
|
||||
|
||||
#[\Override]
|
||||
public function id(): ArcadiaId
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
14
src/Shared/DDD/Model/AggregateRoot.php
Normal file
14
src/Shared/DDD/Model/AggregateRoot.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shared\DDD\Model;
|
||||
|
||||
/**
|
||||
* @template T of EntityId
|
||||
*
|
||||
* @extends Entity<T>
|
||||
*/
|
||||
interface AggregateRoot extends Entity
|
||||
{
|
||||
}
|
||||
19
src/Shared/DDD/Model/ReadModel.php
Normal file
19
src/Shared/DDD/Model/ReadModel.php
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Shared\DDD\Model;
|
||||
|
||||
/**
|
||||
* Interface pour les modèles de lecture et projections en lecture seule.
|
||||
*
|
||||
* Par définition, un modèle de lecture n'est pas modifiable.
|
||||
* Toute implémentation doit être `final readonly class`.
|
||||
*
|
||||
* @template T of EntityId
|
||||
*
|
||||
* @extends Entity<T>
|
||||
*/
|
||||
interface ReadModel extends Entity
|
||||
{
|
||||
}
|
||||
|
|
@ -5,10 +5,10 @@ declare(strict_types=1);
|
|||
namespace Shared\DDD\Port;
|
||||
|
||||
/**
|
||||
* @template TId
|
||||
* @template TEntity
|
||||
* @template TId of \Shared\DDD\Model\EntityId
|
||||
* @template TReadModel of \Shared\DDD\Model\ReadModel
|
||||
*
|
||||
* @method TEntity|null find(TId $id)
|
||||
* @method TReadModel|null find(TId $id)
|
||||
*/
|
||||
interface Directory
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ namespace Shared\DDD\Port;
|
|||
|
||||
/**
|
||||
* @template TId of \Shared\DDD\Model\EntityId
|
||||
* @template TEntity of \Shared\DDD\Model\Entity
|
||||
* @template TAggregate of \Shared\DDD\Model\AggregateRoot
|
||||
*
|
||||
* @method TEntity|null find(TId $id)
|
||||
* @method void persist(TEntity $entity)
|
||||
* @method void delete(TEntity $entity)
|
||||
* @method TAggregate|null find(TId $id)
|
||||
* @method void persist(TAggregate $aggregate)
|
||||
* @method void delete(TAggregate $aggregate)
|
||||
*
|
||||
* @throws \Shared\DDD\Exception\NotFoundException
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@ declare(strict_types=1);
|
|||
|
||||
namespace Tests\Architecture;
|
||||
|
||||
use Shared\DDD\Model\AggregateRoot;
|
||||
use Shared\DDD\Model\Entity;
|
||||
use Shared\DDD\Model\ReadModel;
|
||||
use Shared\DDD\Port\Directory;
|
||||
use Shared\DDD\Port\Repository;
|
||||
|
||||
|
|
@ -93,3 +95,22 @@ describe('Domain Architecture Rules', function (): void {
|
|||
'view',
|
||||
]);
|
||||
});
|
||||
|
||||
describe('AggregateRoot & ReadModel Architecture Rules', function (): void {
|
||||
arch('AggregateRoot implementations are final')
|
||||
->expect(AggregateRoot::class)
|
||||
->classes()
|
||||
->toBeFinal();
|
||||
|
||||
arch('Domain AggregateRoot implementations are readonly')
|
||||
->expect('Domain\Models\Entities')
|
||||
->classes()
|
||||
->toBeReadonly();
|
||||
|
||||
arch('ReadModel implementations are final and readonly')
|
||||
->expect(ReadModel::class)
|
||||
->classes()
|
||||
->toBeFinal()
|
||||
->toBeReadonly()
|
||||
->ignoring(ReadModel::class);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue