feat: ✨ create Campaign entity and associated VOs
This commit is contained in:
parent
b5155c8ade
commit
ab1923c05b
9 changed files with 331 additions and 0 deletions
103
src/Domain/Models/Entities/Campaign.php
Normal file
103
src/Domain/Models/Entities/Campaign.php
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\Entities;
|
||||
use Domain\Enums\Country;
|
||||
use Domain\Models\Entities\Tag;
|
||||
use Domain\Models\ValueObjects\Brand\BrandCode;
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignCode;
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignCoupon;
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignId;
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignName;
|
||||
use Shared\DDD\Models\AggregateRoot;
|
||||
use Shared\DDD\Models\HasTimestampIdentifier;
|
||||
|
||||
/**
|
||||
* @implements \Shared\DDD\Models\AggregateRoot<\Domain\Models\ValueObjects\Campaign\CampaignId>
|
||||
*/
|
||||
final readonly class Campaign implements AggregateRoot, HasTimestampIdentifier
|
||||
{
|
||||
private function __construct(
|
||||
public CampaignId $id,
|
||||
public CampaignCode $code,
|
||||
public Tag $tag,
|
||||
public CampaignName $name,
|
||||
public bool $publication,
|
||||
public bool $printAllAtOnce,
|
||||
public Country $availableIn,
|
||||
/** @var CampaignCoupon[] */
|
||||
public array $coupons,
|
||||
public BrandCode $brand,
|
||||
public ?\DateTimeInterface $updatedAt = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function id(): CampaignId
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function createdAt(): \DateTimeInterface
|
||||
{
|
||||
return $this->id->toDateTime();
|
||||
}
|
||||
|
||||
public function updatedAt(): ?\DateTimeInterface
|
||||
{
|
||||
return $this->updatedAt;
|
||||
}
|
||||
|
||||
public static function create(
|
||||
CampaignCode $code,
|
||||
Tag $tag,
|
||||
CampaignName $name,
|
||||
bool $publication,
|
||||
bool $printAllAtOnce,
|
||||
Country $availableIn,
|
||||
/** @var CampaignCoupon[] */
|
||||
array $coupons,
|
||||
BrandCode $brand,
|
||||
?\DateTimeInterface $updatedAt = null,
|
||||
): Campaign {
|
||||
return new self(
|
||||
id: CampaignId::generate(),
|
||||
code: $code,
|
||||
tag: $tag,
|
||||
name: $name,
|
||||
publication: $publication,
|
||||
printAllAtOnce: $printAllAtOnce,
|
||||
availableIn: $availableIn,
|
||||
coupons: $coupons,
|
||||
brand: $brand,
|
||||
updatedAt: $updatedAt,
|
||||
);
|
||||
}
|
||||
|
||||
public static function hydrate(
|
||||
CampaignId $id,
|
||||
CampaignCode $code,
|
||||
Tag $tag,
|
||||
CampaignName $name,
|
||||
bool $publication,
|
||||
bool $printAllAtOnce,
|
||||
Country $availableIn,
|
||||
/** @var CampaignCoupon[] */
|
||||
array $coupons,
|
||||
BrandCode $brand,
|
||||
?\DateTimeInterface $updatedAt = null,
|
||||
): self {
|
||||
return new self(
|
||||
id: $id,
|
||||
code: $code,
|
||||
tag: $tag,
|
||||
name: $name,
|
||||
publication: $publication,
|
||||
printAllAtOnce: $printAllAtOnce,
|
||||
availableIn: $availableIn,
|
||||
coupons: $coupons,
|
||||
brand: $brand,
|
||||
updatedAt: $updatedAt,
|
||||
);
|
||||
}
|
||||
}
|
||||
18
src/Domain/Models/ValueObjects/Campaign/CampaignCode.php
Normal file
18
src/Domain/Models/ValueObjects/Campaign/CampaignCode.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
final readonly class CampaignCode
|
||||
{
|
||||
public function __construct(
|
||||
public string $code,
|
||||
) {
|
||||
$trimmed = trim($code);
|
||||
|
||||
if ($trimmed === '') {
|
||||
throw new \DomainException('Campaign code cannot be empty.');
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/Domain/Models/ValueObjects/Campaign/CampaignCoupon.php
Normal file
22
src/Domain/Models/ValueObjects/Campaign/CampaignCoupon.php
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
use Domain\Models\ValueObjects\PrintRules;
|
||||
use Domain\Models\ValueObjects\ProcessingCode;
|
||||
|
||||
final readonly class CampaignCoupon
|
||||
{
|
||||
public function __construct(
|
||||
public ProcessingCode $code,
|
||||
public StockId $qty,
|
||||
public ?PrintRules $printRules,
|
||||
public int $warningThresholdQty
|
||||
) {
|
||||
if ($this->warningThresholdQty <= 0) {
|
||||
throw new \DomainException('Campaign Coupon warningThresholdQty must be positive.');
|
||||
}
|
||||
}
|
||||
}
|
||||
16
src/Domain/Models/ValueObjects/Campaign/CampaignId.php
Normal file
16
src/Domain/Models/ValueObjects/Campaign/CampaignId.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
use Ramsey\Uuid\Uuid;
|
||||
use Shared\DDD\Models\Uuidv7EntityId;
|
||||
|
||||
final readonly class CampaignId extends Uuidv7EntityId
|
||||
{
|
||||
public static function generate(): self
|
||||
{
|
||||
return new self(Uuid::uuid7());
|
||||
}
|
||||
}
|
||||
18
src/Domain/Models/ValueObjects/Campaign/CampaignName.php
Normal file
18
src/Domain/Models/ValueObjects/Campaign/CampaignName.php
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
final readonly class CampaignName
|
||||
{
|
||||
public function __construct(
|
||||
public string $value,
|
||||
) {
|
||||
$trimmed = trim($value);
|
||||
|
||||
if ($trimmed === '') {
|
||||
throw new \DomainException('Campaign name cannot be empty.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignCode;
|
||||
|
||||
covers(CampaignCode::class);
|
||||
|
||||
describe('campaign code', function (): void {
|
||||
it('creates a valid campaign code', function (): void {
|
||||
// GIVEN
|
||||
$code = 'Name';
|
||||
|
||||
// WHEN
|
||||
$name = new CampaignCode($code);
|
||||
|
||||
// THEN
|
||||
expect($name->code)->toBe($code);
|
||||
});
|
||||
|
||||
it('rejects invalid campaign code', function (): void {
|
||||
// GIVEN
|
||||
$code = '';
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CampaignCode => new CampaignCode($code))->toThrow(\DomainException::class);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignCoupon;
|
||||
use Domain\Models\ValueObjects\PrintRules;
|
||||
use Domain\Models\ValueObjects\ProcessingCode;
|
||||
|
||||
covers(CampaignCoupon::class);
|
||||
|
||||
describe('campaign coupon', function (): void {
|
||||
it('creates a valid campaign coupon', function (): void {
|
||||
// GIVEN
|
||||
$code = new ProcessingCode('PC1');
|
||||
$qty = new StockId();
|
||||
$printRules = new PrintRules(maxPerCustomer: 3);
|
||||
$warningThresholdQty = 100;
|
||||
|
||||
// WHEN
|
||||
$campaignCoupon = new CampaignCoupon(code: $code, qty: $qty, printRules: $printRules, warningThresholdQty: $warningThresholdQty);
|
||||
|
||||
// THEN
|
||||
expect($campaignCoupon->code)->toBe($code)
|
||||
->and($campaignCoupon->qty)->toBe($qty)
|
||||
->and($campaignCoupon->printRules)->toBe($printRules)
|
||||
->and($campaignCoupon->warningThresholdQty)->toBe($warningThresholdQty);
|
||||
});
|
||||
|
||||
it('creates a valid campaign coupon whitout print rules', function (): void {
|
||||
// GIVEN
|
||||
$code = new ProcessingCode('PC1');
|
||||
$qty = new StockId();
|
||||
$warningThresholdQty = 100;
|
||||
|
||||
// WHEN
|
||||
$campaignCoupon = new CampaignCoupon(code: $code, qty: $qty, warningThresholdQty: $warningThresholdQty);
|
||||
|
||||
// THEN
|
||||
expect($campaignCoupon->code)->toBe($code)
|
||||
->and($campaignCoupon->qty)->toBe($qty)
|
||||
->and($campaignCoupon->warningThresholdQty)->toBe($warningThresholdQty);
|
||||
});
|
||||
|
||||
it('rejects invalid campaign coupon', function (): void {
|
||||
// GIVEN
|
||||
$code = new ProcessingCode('PC1');
|
||||
$qty = new StockId();
|
||||
$printRules = new PrintRules(maxPerCustomer: 3);
|
||||
$warningThresholdQty = -100;
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CampaignCoupon => new CampaignCoupon(code: $code, qty: $qty, printRules: $printRules, warningThresholdQty: $warningThresholdQty))->toThrow(\DomainException::class);
|
||||
});
|
||||
|
||||
it('rejects invalid campaign coupon whitout print rules', function (): void {
|
||||
// GIVEN
|
||||
$code = new ProcessingCode('PC1');
|
||||
$qty = new StockId();
|
||||
$warningThresholdQty = -100;
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CampaignCoupon => new CampaignCoupon(code: $code, qty: $qty, warningThresholdQty: $warningThresholdQty))->toThrow(\DomainException::class);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignId;
|
||||
|
||||
covers(CampaignId::class);
|
||||
|
||||
describe('campaign', function (): void {
|
||||
it('generates a unique campaign id', function (): void {
|
||||
// WHEN
|
||||
$id = CampaignId::generate();
|
||||
|
||||
// THEN
|
||||
expect($id)->toBeInstanceOf(CampaignId::class);
|
||||
});
|
||||
|
||||
it('generates different ids on each call', function (): void {
|
||||
// WHEN
|
||||
$first = CampaignId::generate();
|
||||
$second = CampaignId::generate();
|
||||
|
||||
// THEN
|
||||
expect((string) $first)->not->toBe((string) $second);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Unit\Domain\Models\ValueObjects\Campaign;
|
||||
|
||||
use Domain\Models\ValueObjects\Campaign\CampaignName;
|
||||
|
||||
covers(CampaignName::class);
|
||||
|
||||
describe('campaign name', function (): void {
|
||||
it('creates a valid campaign name', function (): void {
|
||||
// GIVEN
|
||||
$value = 'Name';
|
||||
|
||||
// WHEN
|
||||
$name = new CampaignName($value);
|
||||
|
||||
// THEN
|
||||
expect($name->value)->toBe($value);
|
||||
});
|
||||
|
||||
it('rejects invalid campaign name', function (): void {
|
||||
// GIVEN
|
||||
$value = '';
|
||||
|
||||
// GIVEN / THEN
|
||||
expect(fn (): CampaignName => new CampaignName($value))->toThrow(\DomainException::class);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue