feat: Setup projet Classeo avec infrastructure Docker et architecture DDD

Configure l'environnement de développement complet avec Docker Compose,
structure DDD 4 Bounded Contexts, et pipeline CI/CD GitHub Actions.

Corrections compatibilité CI:
- Symfony 8 nécessite monolog-bundle ^4.0 (la v3.x ne supporte que jusqu'à Symfony 7)
- ESLint v9 nécessite flat config (eslint.config.js) - le format .eslintrc.cjs est obsolète
This commit is contained in:
2026-01-30 09:55:58 +01:00
parent ddefa927c7
commit 6da5996340
125 changed files with 10032 additions and 0 deletions

View File

@@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Shared\Domain;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\DomainEvent;
use DateTimeImmutable;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;
final class AggregateRootTest extends TestCase
{
public function testPullDomainEventsReturnsRecordedEvents(): void
{
$aggregate = new TestAggregate();
$event1 = new TestDomainEvent('test1');
$event2 = new TestDomainEvent('test2');
$aggregate->doSomething($event1);
$aggregate->doSomething($event2);
$events = $aggregate->pullDomainEvents();
$this->assertCount(2, $events);
$this->assertSame($event1, $events[0]);
$this->assertSame($event2, $events[1]);
}
public function testPullDomainEventsClearsEventsAfterPulling(): void
{
$aggregate = new TestAggregate();
$event = new TestDomainEvent('test');
$aggregate->doSomething($event);
$aggregate->pullDomainEvents();
$secondPull = $aggregate->pullDomainEvents();
$this->assertCount(0, $secondPull);
}
public function testRecordEventAddsEventToInternalList(): void
{
$aggregate = new TestAggregate();
$event = new TestDomainEvent('test');
$aggregate->doSomething($event);
$events = $aggregate->pullDomainEvents();
$this->assertCount(1, $events);
$this->assertInstanceOf(TestDomainEvent::class, $events[0]);
}
}
// Test implementations
final class TestAggregate extends AggregateRoot
{
public function doSomething(DomainEvent $event): void
{
$this->recordEvent($event);
}
}
final readonly class TestDomainEvent implements DomainEvent
{
private DateTimeImmutable $occurredOn;
public function __construct(
public string $data,
private ?UuidInterface $testAggregateId = null,
) {
$this->occurredOn = new DateTimeImmutable();
}
public function occurredOn(): DateTimeImmutable
{
return $this->occurredOn;
}
public function aggregateId(): UuidInterface
{
return $this->testAggregateId ?? Uuid::uuid4();
}
}

View File

@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Shared\Domain;
use App\Shared\Domain\CorrelationId;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;
final class CorrelationIdTest extends TestCase
{
public function testGenerateCreatesValidUuid(): void
{
$correlationId = CorrelationId::generate();
$this->assertTrue(Uuid::isValid($correlationId->value()));
}
public function testFromStringCreatesCorrelationIdFromValidUuid(): void
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$correlationId = CorrelationId::fromString($uuid);
$this->assertSame($uuid, $correlationId->value());
}
public function testValueReturnsUuidString(): void
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$correlationId = CorrelationId::fromString($uuid);
$this->assertSame($uuid, $correlationId->value());
}
public function testToStringReturnsUuidString(): void
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$correlationId = CorrelationId::fromString($uuid);
$this->assertSame($uuid, (string) $correlationId);
}
public function testGenerateCreatesDifferentIdsEachTime(): void
{
$id1 = CorrelationId::generate();
$id2 = CorrelationId::generate();
$this->assertNotSame($id1->value(), $id2->value());
}
}

View File

@@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
namespace App\Tests\Unit\Shared\Domain;
use App\Shared\Domain\EntityId;
use PHPUnit\Framework\TestCase;
use Ramsey\Uuid\Uuid;
final class EntityIdTest extends TestCase
{
public function testGenerateCreatesValidUuid(): void
{
$id = TestEntityId::generate();
$this->assertInstanceOf(TestEntityId::class, $id);
$this->assertTrue(Uuid::isValid((string) $id));
}
public function testFromStringCreatesEntityIdFromValidUuid(): void
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$id = TestEntityId::fromString($uuid);
$this->assertSame($uuid, (string) $id);
}
public function testEqualsReturnsTrueForSameValue(): void
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$id1 = TestEntityId::fromString($uuid);
$id2 = TestEntityId::fromString($uuid);
$this->assertTrue($id1->equals($id2));
}
public function testEqualsReturnsFalseForDifferentValue(): void
{
$id1 = TestEntityId::generate();
$id2 = TestEntityId::generate();
$this->assertFalse($id1->equals($id2));
}
public function testToStringReturnsUuidString(): void
{
$uuid = '550e8400-e29b-41d4-a716-446655440000';
$id = TestEntityId::fromString($uuid);
$this->assertSame($uuid, (string) $id);
}
}
// Test concrete implementation
final readonly class TestEntityId extends EntityId
{
}

View File

@@ -0,0 +1,13 @@
<?php
declare(strict_types=1);
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__) . '/vendor/autoload.php';
if (file_exists(dirname(__DIR__) . '/config/bootstrap.php')) {
require dirname(__DIR__) . '/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__) . '/.env');
}