Corak Model Domain
Gambaran Keseluruhan
Section titled “Gambaran Keseluruhan”Corak Model Domain mewakili konsep perniagaan, peraturan dan logik aplikasi anda. Dalam XOOPS pembangunan modul, model domain merangkumi entiti perniagaan teras dan gelagat mereka.
Entiti lwn Objek Nilai
Section titled “Entiti lwn Objek Nilai”Entiti
Section titled “Entiti”Entiti mempunyai identiti dan kitaran hayat:
<?php
declare(strict_types=1);
namespace XoopsModules\MyModule\Entity;
use XoopsModules\MyModule\ValueObject\ArticleId;
final class Article{ private bool $isNew = true;
public function __construct( private ArticleId $id, private string $title, private string $content, private int $authorId, private int $categoryId, private ArticleStatus $status, private \DateTimeImmutable $createdAt, private ?\DateTimeImmutable $updatedAt = null ) {}
public static function create( string $title, string $content, int $authorId, int $categoryId ): self { return new self( id: ArticleId::generate(), title: $title, content: $content, authorId: $authorId, categoryId: $categoryId, status: ArticleStatus::DRAFT, createdAt: new \DateTimeImmutable() ); }
public function publish(): void { if ($this->status === ArticleStatus::PUBLISHED) { throw new \DomainException('Article is already published'); }
$this->status = ArticleStatus::PUBLISHED; $this->updatedAt = new \DateTimeImmutable(); }
public function updateContent(string $title, string $content): void { $this->title = $title; $this->content = $content; $this->updatedAt = new \DateTimeImmutable(); }
// Getters... public function getId(): ArticleId { return $this->id; } public function getTitle(): string { return $this->title; } public function getContent(): string { return $this->content; } public function getStatus(): ArticleStatus { return $this->status; } public function isNew(): bool { return $this->isNew; }
public function markAsPersisted(): void { $this->isNew = false; }}Objek Nilai
Section titled “Objek Nilai”Objek Nilai tidak boleh diubah dan dibandingkan dengan nilai:
<?php
declare(strict_types=1);
namespace XoopsModules\MyModule\ValueObject;
final class ArticleId{ private function __construct( private readonly string $value ) {}
public static function generate(): self { return new self(\Xmf\Ulid::generate()); }
public static function fromString(string $value): self { if (empty($value)) { throw new \InvalidArgumentException('ArticleId cannot be empty'); } return new self($value); }
public function toString(): string { return $this->value; }
public function equals(self $other): bool { return $this->value === $other->value; }}Agregat
Section titled “Agregat”Agregat ialah kelompok objek domain yang dianggap sebagai satu unit:
final class Category{ private array $articles = [];
public function __construct( private CategoryId $id, private string $name, private ?CategoryId $parentId = null ) {}
public function addArticle(Article $article): void { if ($article->getCategoryId() !== $this->id->toInt()) { throw new \DomainException('Article does not belong to this category'); } $this->articles[] = $article; }
public function getArticleCount(): int { return count($this->articles); }}Acara Domain
Section titled “Acara Domain”Tangkap kejadian domain penting:
final class ArticlePublishedEvent{ public function __construct( public readonly ArticleId $articleId, public readonly int $authorId, public readonly \DateTimeImmutable $publishedAt ) {}}Enum untuk Status
Section titled “Enum untuk Status”Gunakan PHP 8.2+ enum untuk nilai status selamat jenis:
enum ArticleStatus: string{ case DRAFT = 'draft'; case PENDING_REVIEW = 'pending'; case PUBLISHED = 'published'; case ARCHIVED = 'archived';
public function canTransitionTo(self $newStatus): bool { return match($this) { self::DRAFT => in_array($newStatus, [self::PENDING_REVIEW, self::ARCHIVED]), self::PENDING_REVIEW => in_array($newStatus, [self::DRAFT, self::PUBLISHED]), self::PUBLISHED => $newStatus === self::ARCHIVED, self::ARCHIVED => false, }; }}Invarian
Section titled “Invarian”Lindungi peraturan domain dalam entiti:
final class Article{ public function setTitle(string $title): void { if (strlen($title) < 5) { throw new \DomainException('Title must be at least 5 characters'); } if (strlen($title) > 255) { throw new \DomainException('Title cannot exceed 255 characters'); } $this->title = $title; }
public function archive(): void { if ($this->status === ArticleStatus::DRAFT) { throw new \DomainException('Cannot archive a draft article'); } $this->status = ArticleStatus::ARCHIVED; }}Amalan Terbaik
Section titled “Amalan Terbaik”- Model Domain Kaya - Letakkan gelagat dalam entiti, bukan perkhidmatan
- Objek Nilai Tidak Boleh Berubah - Objek nilai tidak boleh berubah
- Kaedah Kilang - Gunakan kaedah kilang statik untuk pembinaan yang kompleks
- Klausa Pengawal - Sahkan input di sempadan entiti
- Peristiwa Domain - Tangkap perubahan keadaan yang ketara
- Bahasa Ubiquitous - Gunakan istilah perniagaan dalam kod
Dokumentasi Berkaitan
Section titled “Dokumentasi Berkaitan”- Lapisan Perkhidmatan - Perkhidmatan aplikasi
- Lapisan Repositori - Kegigihan
- DTO-Corak - Pemindahan data
- Sistem Peristiwa - Acara domain