تخطَّ إلى المحتوى

نمط نموذج المجال

يمثل نمط نموذج المجال المفاهيم والقواعد والمنطق التجاري لتطبيقك. في تطوير وحدات XOOPS، تحتوي نماذج المجال على كيانات الأعمال الأساسية وسلوكياتها.

الكيان مقابل كائن القيمة

Section titled “الكيان مقابل كائن القيمة”

الكيانات لها هوية ودورة حياة:

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

كائنات القيمة ثابتة وتتم مقارنتها حسب القيمة:

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

التجميعات عبارة عن مجموعات من كائنات المجال يتم التعامل معها كوحدة واحدة:

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

تسجيل حدث المجال الهامة:

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

استخدم تعدادات PHP 8.2+ للقيم الآمنة للحالة:

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

حماية قواعد المجال داخل الكيانات:

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. نموذج مجال غني - ضع السلوك في الكيانات وليس في الخدمات
  2. كائنات قيمة ثابتة - يجب ألا تتغير كائنات القيمة أبداً
  3. طرق المصنع - استخدم طرق مصنع ثابتة للبناء المعقد
  4. جملات الحراسة - تحقق من المدخلات عند حدود الكيانات
  5. أحداث المجال - احتفظ بتغييرات الحالة المهمة
  6. اللغة العالمية - استخدم المصطلحات التجارية في الكود
  • Service-Layer - خدمات التطبيق
  • Repository-Layer - الثبات
  • DTO-Pattern - نقل البيانات
  • Event-System - أحداث المجال