Files
Classeo/backend/src/SuperAdmin/Domain/Model/Establishment/Establishment.php
Mathias STRASSER 18c54e6d67
Some checks failed
CI / Backend Tests (push) Has been cancelled
CI / Frontend Tests (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
CI / Naming Conventions (push) Has been cancelled
CI / Build Check (push) Has been cancelled
feat: Provisionner automatiquement un nouvel établissement
Lorsqu'un super-admin crée un établissement via l'interface, le système
doit automatiquement créer la base tenant, exécuter les migrations,
créer le premier utilisateur admin et envoyer l'invitation — le tout
de manière asynchrone pour ne pas bloquer la réponse HTTP.

Ce mécanisme rend chaque établissement opérationnel dès sa création
sans intervention manuelle sur l'infrastructure.
2026-04-11 20:52:41 +02:00

124 lines
3.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\SuperAdmin\Domain\Model\Establishment;
use App\Shared\Domain\AggregateRoot;
use App\Shared\Domain\Tenant\TenantId;
use App\SuperAdmin\Domain\Event\EtablissementCree;
use App\SuperAdmin\Domain\Event\EtablissementDesactive;
use App\SuperAdmin\Domain\Exception\EstablishmentDejaInactifException;
use App\SuperAdmin\Domain\Model\SuperAdmin\SuperAdminId;
use DateTimeImmutable;
use function sprintf;
/**
* Aggregate Root for an Establishment (tenant) — lives in master database.
*
* Each Establishment maps to a tenant with its own database.
*/
final class Establishment extends AggregateRoot
{
public private(set) ?DateTimeImmutable $lastActivityAt = null;
private function __construct(
public private(set) EstablishmentId $id,
public private(set) TenantId $tenantId,
public private(set) string $name,
public private(set) string $subdomain,
public private(set) string $databaseName,
public private(set) EstablishmentStatus $status,
public private(set) DateTimeImmutable $createdAt,
public private(set) ?SuperAdminId $createdBy,
) {
}
public static function creer(
string $name,
string $subdomain,
string $adminEmail,
SuperAdminId $createdBy,
DateTimeImmutable $createdAt,
): self {
$tenantId = TenantId::generate();
$establishment = new self(
id: EstablishmentId::generate(),
tenantId: $tenantId,
name: $name,
subdomain: $subdomain,
databaseName: sprintf('classeo_tenant_%s', str_replace('-', '', (string) $tenantId)),
status: EstablishmentStatus::PROVISIONING,
createdAt: $createdAt,
createdBy: $createdBy,
);
$establishment->recordEvent(new EtablissementCree(
establishmentId: $establishment->id,
tenantId: $establishment->tenantId,
name: $name,
subdomain: $subdomain,
adminEmail: $adminEmail,
occurredOn: $createdAt,
));
return $establishment;
}
public function activer(): void
{
$this->status = EstablishmentStatus::ACTIF;
}
public function desactiver(DateTimeImmutable $at): void
{
if ($this->status !== EstablishmentStatus::ACTIF) {
throw EstablishmentDejaInactifException::pour($this->id);
}
$this->status = EstablishmentStatus::INACTIF;
$this->recordEvent(new EtablissementDesactive(
establishmentId: $this->id,
occurredOn: $at,
));
}
public function enregistrerActivite(DateTimeImmutable $at): void
{
$this->lastActivityAt = $at;
}
/**
* @internal For Infrastructure use only
*/
public static function reconstitute(
EstablishmentId $id,
TenantId $tenantId,
string $name,
string $subdomain,
string $databaseName,
EstablishmentStatus $status,
DateTimeImmutable $createdAt,
?SuperAdminId $createdBy = null,
?DateTimeImmutable $lastActivityAt = null,
): self {
$establishment = new self(
id: $id,
tenantId: $tenantId,
name: $name,
subdomain: $subdomain,
databaseName: $databaseName,
status: $status,
createdAt: $createdAt,
createdBy: $createdBy,
);
$establishment->lastActivityAt = $lastActivityAt;
return $establishment;
}
}