Entity Creation Guide
Anleitung zum Erstellen von Entities in der Appiyon-Plattform.
Übersicht
Entities sind die Kern-Datenstrukturen der Anwendung. Sie repräsentieren persistente Domänen-Objekte.
Layer-Zuordnung
Entities werden je nach Funktion in verschiedenen Layern platziert:
- Infrastructure: System-Administration (Admin, AdminSession, etc.)
- Shared: Gemeinsame Utilities (AuditLog)
- Foundation: Basis-Bausteine (Tenant, User)
- Core: Kontext-freie Primitives (Country, Language)
- Domain: Business-Logik (App, Developer, Category, etc.)
Entity-Struktur
Basis-Template
php
<?php
declare(strict_types=1);
namespace App\Appi\[Layer]\[Module]\Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
#[ORM\Table(name: 'table_name')]
class EntityName
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: Types::INTEGER)]
private ?int $id = null;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $name;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $createdAt;
public function __construct(string $name)
{
$this->name = $name;
$this->createdAt = new \DateTimeImmutable();
}
public function getId(): ?int
{
return $this->id;
}
public function getName(): string
{
return $this->name;
}
public function setName(string $name): void
{
$this->name = $name;
}
public function getCreatedAt(): \DateTimeImmutable
{
return $this->createdAt;
}
}Wichtige Patterns
1. Soft Deletes
Für Audit-Trail-Integrität sollten wichtige Entities Soft Deletes unterstützen:
php
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $deletedAt = null;
public function delete(): void
{
$this->deletedAt = new \DateTimeImmutable();
}
public function isDeleted(): bool
{
return $this->deletedAt !== null;
}2. Timestamps
Standard-Timestamps für Created/Updated:
php
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $createdAt;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $updatedAt;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
$this->updatedAt = new \DateTimeImmutable();
}
public function touch(): void
{
$this->updatedAt = new \DateTimeImmutable();
}3. Unique Constraints
php
#[ORM\Entity]
#[ORM\Table(name: 'admins')]
#[ORM\UniqueConstraint(name: 'admins_email_unique', columns: ['email'])]
class Admin
{
#[ORM\Column(type: Types::STRING, length: 255)]
private string $email;
}4. Indices für Performance
php
#[ORM\Entity]
#[ORM\Table(name: 'admin_sessions')]
#[ORM\Index(name: 'idx_admin_sessions_admin_id', columns: ['admin_id'])]
#[ORM\Index(name: 'idx_admin_sessions_last_activity', columns: ['last_activity'])]
class AdminSession
{
// ...
}5. Foreign Keys mit Relations
php
#[ORM\ManyToOne(targetEntity: Admin::class)]
#[ORM\JoinColumn(name: 'admin_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
private Admin $admin;6. ULID Support
php
use Symfony\Component\Uid\Ulid;
#[ORM\Column(type: 'ulid', unique: true)]
private Ulid $ulid;
public function __construct()
{
$this->ulid = new Ulid();
}ID-Strategien
IDENTITY (Auto-Increment)
Standard für PostgreSQL:
php
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: Types::INTEGER)]
private ?int $id = null;UUID/ULID
Für verteilte Systeme:
php
use Symfony\Component\Uid\Ulid;
#[ORM\Id]
#[ORM\Column(type: 'ulid')]
private Ulid $id;
public function __construct()
{
$this->id = new Ulid();
}Value Objects in Entities
Entities sollten Value Objects für komplexe Validierung nutzen:
php
use App\Appi\Infrastructure\Admin\ValueObject\AdminEmail;
use App\Appi\Infrastructure\Admin\ValueObject\AdminPassword;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $email;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $password;
public function __construct(
string $name,
AdminEmail $email,
AdminPassword $password
) {
$this->name = $name;
$this->email = $email->getValue();
$this->password = $password->getHash();
}Migration erstellen
Nach Entity-Erstellung:
bash
php bin/console doctrine:migrations:diffDies erstellt automatisch eine Migration im entsprechenden Layer-Ordner.
Best Practices
- Immutability: Konstruktor setzt initiale Werte, Setter nur wo nötig
- Type Safety: Immer strict types verwenden
- Validation: In Value Objects auslagern, nicht in Entity
- Business Logic: In Use Cases, nicht in Entity
- Naming: Singular für Entity-Namen (Admin, nicht Admins)
- Table Names: Plural für Tabellen-Namen (admins, nicht admin)
Beispiel: Admin Entity
Siehe vollständiges Beispiel in: src/Appi/Infrastructure/Admin/Entity/Admin.php
php
#[ORM\Entity]
#[ORM\Table(name: 'admins')]
#[ORM\UniqueConstraint(name: 'admins_email_unique', columns: ['email'])]
class Admin
{
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'IDENTITY')]
#[ORM\Column(type: Types::INTEGER)]
private ?int $id = null;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $name;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $email;
#[ORM\Column(type: Types::STRING, length: 255)]
private string $password;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $emailVerifiedAt = null;
#[ORM\Column(type: Types::STRING, length: 100, nullable: true)]
private ?string $rememberToken = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE, nullable: true)]
private ?\DateTimeImmutable $deletedAt = null;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $createdAt;
#[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
private \DateTimeImmutable $updatedAt;
// Constructor, Getters, Setters...
}Checkliste
- [ ] Namespace korrekt für Layer und Modul
- [ ]
declare(strict_types=1);am Dateianfang - [ ] ORM Attributes korrekt gesetzt
- [ ] Indices für häufige Queries
- [ ] Unique Constraints wo nötig
- [ ] Soft Delete wenn Audit-relevant
- [ ] Timestamps (createdAt, updatedAt)
- [ ] Constructor mit Required Fields
- [ ] Value Objects für Validierung genutzt
- [ ] PHPDoc für komplexe Typen
- [ ] Migration erstellt und getestet