Bỏ qua để đến nội dung

Mẫu mô hình miền

Mẫu Mô hình miền thể hiện các khái niệm, quy tắc và logic kinh doanh của ứng dụng của bạn. Trong quá trình phát triển mô-đun XOOPS, các mô hình miền đóng gói các thực thể kinh doanh cốt lõi và hành vi của chúng.

Các thực thể có danh tính và vòng đời:

<?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;
}
}

Đối tượng giá trị là bất biến và được so sánh theo giá trị:

<?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;
}
}

Các tập hợp là các cụm đối tượng miền được coi là một đơn vị:

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);
}
}

Ghi lại các sự kiện quan trọng trong miền:

final class ArticlePublishedEvent
{
public function __construct(
public readonly ArticleId $articleId,
public readonly int $authorId,
public readonly \DateTimeImmutable $publishedAt
) {}
}

Sử dụng enum PHP 8.2+ cho các giá trị trạng thái an toàn loại:

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,
};
}
}

Bảo vệ các quy tắc miền trong các thực thể:

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;
}
}
  1. Mô hình miền phong phú - Đặt hành vi vào các thực thể, không phải dịch vụ
  2. Đối tượng giá trị bất biến - Đối tượng giá trị không bao giờ được thay đổi
  3. Phương pháp nhà máy - Sử dụng phương pháp nhà máy tĩnh cho công trình phức tạp
  4. Điều khoản bảo vệ - Xác thực đầu vào ở ranh giới thực thể
  5. Sự kiện miền - Ghi lại những thay đổi trạng thái quan trọng
  6. Ngôn ngữ phổ biến - Sử dụng thuật ngữ kinh doanh trong mã
  • Tầng dịch vụ - Dịch vụ ứng dụng
  • Lớp lưu trữ - Persistence
  • DTO-Pattern - Truyền dữ liệu
  • Hệ thống sự kiện - Sự kiện miền