پرش به مطلب اصلی

سیستم ComputedValues - راهنمای توسعه‌دهنده

نمای کلی معماری

سیستم ComputedValues بر اساس اصول معماری رویدادمحور با تفکیک وظایف روشن و پایبندی به اصول SOLID ساخته شده است.

الگوهای طراحی

الگوی استراتژی

حالت پردازش (همزمان/ناهمزمان) در زمان اجرا بر اساس پیکربندی تعیین می‌شود.

  • ComputedValueManager به استراتژی‌های پردازش همزمان یا ناهمزمان واگذار می‌کند
  • امکان جابجایی بین پردازش فوری و صف‌بندی شده بدون تغییر کد

الگوی ناظر

معماری رویدادمحور با استفاده از سیستم رویداد لاراول.

  • ComputedValueEventSubscriber هم رویدادهای BusinessEvents و هم رویدادهای Eloquent را مشاهده می‌کند
  • اتصال شل بین منابع رویداد و به‌روزرسانی مقادیر محاسبه شده

الگوی آداپتور

EloquentEventAdapter رویدادهای Eloquent را به EventDTO استاندارد تبدیل می‌کند.

  • رویدادهای مدل Eloquent را به فرمت رویداد یکپارچه تبدیل می‌کند
  • پردازش سازگار را صرف‌نظر از منبع رویداد امکان‌پذیر می‌کند

الگوی متد قالب

ویژگی HasComputedValuesField الگوریتم اسکلتی را تعریف می‌کند.

  • ویژگی عملکرد پایه را با نقاط گسترش ارائه می‌دهد
  • مدل‌ها رفتار را از طریق متد پیکربندی سفارشی می‌کنند

اصول SOLID

هر کلاس مسئولیت واحد و به خوبی تعریف شده دارد:

  • ComputedValueManager: جریان پردازش را هماهنگ می‌کند
  • ComputedValueStorageService: ماندگاری پایگاه داده را مدیریت می‌کند
  • ComputedValueCastingService: تبدیل نوع را مدیریت می‌کند
  • ModelDiscoveryService: پیکربندی‌ها را کشف و نگاشت می‌کند
  • EloquentEventAdapter: رویدادهای Eloquent را به DTO تبدیل می‌کند

اجزای اصلی

۱. ComputedValueManager

مکان: app/Services/ComputedValues/Services/ComputedValueManager.php

مسئولیت‌ها:

  • فرآیند به‌روزرسانی مقدار محاسبه شده را هماهنگ می‌کند
  • کلاس‌های کنترل‌کننده را با استفاده از قرارداد نام‌گذاری تفکیک می‌کند
  • کنترل‌کننده‌ها را با تزریق وابستگی اجرا می‌کند
  • به پردازش همزمان یا ناهمزمان بر اساس پیکربندی مسیر می‌دهد
  • متدهای دسترسی به داده را برای مقادیر محاسبه شده ارائه می‌دهد

الگوریتم تفکیک کنترل‌کننده

ورودی: ComputedValueEvent, EntityClass

1. استخراج مسیر ماژول از فضای نام کلاس موجودیت
- مثال: App\Modules\CMS\Entities\PostCMS
- مثال: App\Modules\LMS\Product\Entities\ProductLMS\Product

2. ساخت نام کنترل‌کننده از کلید مقدار محاسبه شده
- تبدیل snake_case به StudlyCase
- الحاق 'ComputedValueHandler'
- مثال: media_count → MediaCountComputedValueHandler

3. امتحان مسیرها با اولویت:
اولویت ۱: زیرپوشه موجودیت
- App\Modules\{Module}\Services\ComputedValueHandlers\{Entity}\{Handler}
- اجازه نام‌های کلیدی یکسان در موجودیت‌های مختلف

اولویت ۲: مسیر مستقیم
- App\Modules\{Module}\Services\ComputedValueHandlers\{Handler}
- برای کنترل‌کننده‌های منحصر به فرد

همچنین پشتیبانی می‌کند:
- App\Core\{Module}\Services\ComputedValueHandlers\{Handler}
- App\Services\ComputedValueHandlers\{Module}\{Handler}

4. بازگرداندن اولین کلاس موجود یا پرتاب استثنا

متدهای کلیدی

متدهدف
processComputedValueUpdate()نقطه ورودی اصلی برای پردازش
executeComputedValueHandler()تفکیک و اجرای کنترل‌کننده
buildHandlerClassName()ساخت نام کلاس کنترل‌کننده
extractModulePathFromEntityClass()تجزیه فضای نام موجودیت
processSyncUpdate()مدیریت پردازش همزمان
processAsyncUpdate()ارسال کار ناهمزمان

۲. ComputedValueStorageService

مکان: app/Services/ComputedValues/Services/ComputedValueStorageService.php

مسئولیت‌ها:

  • نتایج مقدار محاسبه شده را در پایگاه داده ماندگار می‌کند
  • قفل‌گذاری خوش‌بینانه را برای پیشگیری از شرایط رقابتی پیاده‌سازی می‌کند
  • تراکنش‌های پایگاه داده را برای اتمیcity مدیریت می‌کند
  • متدهای بازیابی و پاک کردن داده را ارائه می‌دهد

ساختار ذخیره‌سازی

ستون JSON computed_values داده را در این قالب ذخیره می‌کند:

{
"media_count": {
"data": 42,
"updated_at": "2024-01-15T10:30:00+00:00"
},
"view_statistics": {
"data": {"total": 1523, "today": 42},
"updated_at": "2024-01-15T10:35:00+00:00"
}
}

پیشگیری از شرایط رقابتی

// الگوریتم قفل‌گذاری خوش‌بینانه
1. دریافت داده موجود با برچسب زمانی
2. تجزیه هر دو برچسب زمانی (موجود و فعلی)
3. مقایسه برچسب‌های زمانی:
if (existingTimestamp > currentTimestamp) {
// نادیده گرفتن به‌روزرسانی - داده جدیدتر وجود دارد
لاگ هشدار با تفاوت زمانی
return
}
4. به‌روزرسانی با داده جدید و برچسب زمانی
5. بسته‌بندی در تراکنش پایگاه داده
هشدارهای شرایط رقابتی

هشدارهای شرایط رقابتی در لاگ‌ها عادی هستند و نشان می‌دهند سیستم درست کار می‌کند. آنها به این معنی هستند که به‌روزرسانی جدیدتری از قبل وجود دارد که از بازنویسی داده تازه با داده قدیمی جلوگیری می‌کند.

متدهای کلیدی

متدهدف
storeComputedValueResults()متد ذخیره‌سازی اصلی با تراکنش
processStorageTransaction()پردازش دسته نتایج
updateEntityComputedValue()به‌روزرسانی موجودیت واحد با قفل‌گذاری
getExistingEntities()بازیابی موجودیت‌ها بر اساس شناسه
getComputedValueData()بازیابی مقدار محاسبه شده خاص
clearComputedValueData()حذف ورودی مقدار محاسبه شده
رفتار ذخیره‌سازی
  • داده‌ها به همان شکلی که هستند بدون تبدیل ذخیره می‌شوند
  • تبدیل هنگام خواندن توسط ویژگی HasComputedValuesField اعمال می‌شود
  • از withBypassedComputedValueProtection() برای به‌روزرسانی‌های مشروع استفاده می‌کند
  • همه عملیات در تراکنش‌های پایگاه داده بسته‌بندی می‌شوند

۳. ModelDiscoveryService

مکان: app/Services/ComputedValues/Services/ModelDiscoveryService.php

مسئولیت‌ها:

  • تمام مدل‌هایی را که HasComputedValues را پیاده‌سازی می‌کنند کشف می‌کند
  • نگاشت‌های رویداد به پیکربندی را می‌سازد
  • نتایج کشف را برای عملکرد کش می‌کند
  • هم رویدادهای BusinessEvents و هم رویدادهای Eloquent را مدیریت می‌کند

الگوریتم کشف

دایرکتوری‌های اسکن شده:

  • app/Models/
  • app/Modules/*/Entities/
  • app/Modules/*/Models/
  • app/Core/*/Entities/
  • app/Core/*/Models/

مدیریت کش

ویژگیمقدار
کلید کشcomputed_values_system:model_discovery
TTL86400 ثانیه (24 ساعت)
درایور کشدرایور کش پیش‌فرض Laravel

متدهای کلیدی

متدهدف
discoverModelsWithComputedValues()متد کشف اصلی
getDiscoveryWithFallback()کشف امن با بازگشت
clearDiscoveryCache()پاک کردن کش کشف
findModelsWithComputedValues()اسکن دایرکتوری‌ها
scanDirectoryForClasses()اسکن دایرکتوری واحد
processModelClass()پردازش مدل واحد
resolveEventIdentifier()تفکیک رویداد به شناسه

۴. ComputedValueCastingService

مکان: app/Services/ComputedValues/Services/ComputedValueCastingService.php

مسئولیت‌ها:

  • داده‌ها را به انواع مشخص هنگام خواندن تبدیل می‌کند
  • موارد لبه تبدیل نوع را مدیریت می‌کند
  • اجبار نوع هوشمند را ارائه می‌دهد
  • شکست‌های تبدیل را با زمینه لاگ می‌کند

انواع تبدیل پشتیبانی شده

نوع PHP: array

مثال‌های تبدیل:

[1, 2, 3][1, 2, 3]
'{"a":1}'['a' => 1]
stdClass → (array) $object

مدیریت خطا

  • ComputedValueCastingException را در صورت شکست پرتاب می‌کند
  • زمینه کامل شامل نوع داده و نوع تبدیل را لاگ می‌کند
  • شامل ردیابی پشته برای اشکال‌زدایی

۵. EloquentEventAdapter

مکان: app/Services/ComputedValues/Services/EloquentEventAdapter.php

مسئولیت‌ها:

  • رویدادهای مدل Eloquent را به فرمت EventDTO تبدیل می‌کند
  • payload رویداد جامع را می‌سازد
  • شناسه‌های همبستگی برای ردیابی تولید می‌کند
  • فیلتر به‌روزرسانی شرطی را ارائه می‌دهد

فرآیند تبدیل

ساختار payload

[
'model_class' => 'App\Modules\CMS\Entities\Post',
'model_id' => 123,
'event_type' => 'updated',
'attributes' => ['id' => 123, 'title' => 'عنوان جدید', ...],
'original' => ['title' => 'عنوان قدیمی', ...], // برای به‌روزرسانی‌ها
'changes' => ['title' => 'عنوان جدید'], // برای به‌روزرسانی‌ها
'soft_deleted' => false // در صورت وجود
]

متدهای کلیدی

متدهدف
adaptToEventDTO()متد تبدیل اصلی
buildEventName()ساخت نام رویداد
buildPayload()ساخت payload جامع
generateCorrelationId()ایجاد شناسه همبستگی
shouldTriggerUpdate()بررسی به‌روزرسانی شرطی

جریان پردازش رویداد

توسعه کنترل‌کننده

قالب

MediaStatsComputedValueHandler.php
final class MediaStatsComputedValueHandler implements ComputedValueHandler
{
public function __construct(
private readonly LoggerInterface $logger
) {}

public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$payload = $eventDto->getPayload();
$postId = $payload->get('post_id');

if ($postId === null) {
return new ComputedValueResultCollection([]);
}

$count = MediaLibrary::where('post_id', $postId)->count();

return new ComputedValueResultCollection([
new ComputedValueResult($postId, $count),
]);
}
}

بهترین شیوه‌ها

public function __construct(
private readonly LoggerInterface $logger,
private readonly SomeRepository $repository
) {}

پیکربندی‌های پیشرفته

منابع رویداد چندگانه

new ComputedValueConfig(
key: 'engagement_score',
relatedEvents: [
PostViewedEvent::class, // رویداد کسب‌وکار
CommentAddedEvent::class, // رویداد کسب‌وکار
new EloquentEventConfig( // رویداد Eloquent
modelClass: Comment::class,
events: [
EloquentEventEnum::CREATED,
EloquentEventEnum::DELETED
]
),
],
mode: ComputedValueModeEnum::ASYNC,
cast: ComputedValueCastEnum::Float
)

پردازش شرطی

public function handle(ComputedValueEvent $eventDto): ComputedValueResultCollection
{
$changes = $eventDto->getPayload()->get('changes', []);

// فقط در صورت تغییر ویژگی‌های خاص پردازش کن
if (!isset($changes['status']) && !isset($changes['published_at'])) {
return new ComputedValueResultCollection([]);
}

// ادامه پردازش...
}

ساختارهای داده پیچیده

return new ComputedValueResultCollection([
new ComputedValueResult($postId, [
'views' => ['total' => 1523, 'today' => 42],
'engagement' => ['likes' => 89, 'comments' => 23],
'metadata' => ['last_calculated' => now()->toIso8601String()],
]),
]);

بهینه‌سازی عملکرد

۱. از ASYNC برای محاسبات سنگین استفاده کنید

mode: ComputedValueModeEnum::ASYNC

۲. مدل‌های خاص را هدف قرار دهید

// ✅ کارآمد - فقط به‌روزرسانی‌های MediaLibrary
new EloquentEventConfig(
modelClass: MediaLibrary::class,
events: [EloquentEventEnum::UPDATED]
)

۳. عملیات دسته‌ای

چندین موجودیت را در اجرای کنترل‌کننده واحد پردازش کنید.

۴. پس از تغییرات پیکربندی کشف را اجرا کنید

# کشف به طور خودکار قبل از اسکن کش را پاک می‌کند
php artisan computed-values:discover

۵. کارگران صف را پیکربندی کنید

php artisan queue:work --queue=computed-values --tries=3

اشکال‌زدایی

بررسی کشف

php artisan computed-values:discover

بازرسی مقادیر محاسبه شده

$post = Post::find(1);
dd($post->getAllCacheData());
dd($post->getCacheStatistics());
dd($post->getComputedValueConfigSummary());

پایش صف

php artisan queue:failed
php artisan queue:retry all

فعال کردن لاگ اشکال‌زدایی

$this->logger->debug('پردازش', [
'payload' => $eventDto->getPayload()->toArray(),
]);

تست

تست واحد کنترل‌کننده

public function test_handler_computes_correctly()
{
$event = new ComputedValueEvent(
eventName: 'MediaCreated',
eventClass: MediaUploadedEvent::class,
originalEventData: new EventDTO(...),
cacheKey: 'media_stats'
);

$handler = new MediaStatsComputedValueHandler();
$results = $handler->handle($event);

$this->assertInstanceOf(ComputedValueResultCollection::class, $results);
}

تست یکپارچه

public function test_computed_value_updates_on_event()
{
$post = Post::factory()->create();

event(new MediaUploadedEvent($media));

$post->refresh();
$this->assertTrue($post->hasComputedValue('media_stats'));
$this->assertEquals(1, $post->media_stats);
}

گسترش سیستم

اضافه کردن نوع تبدیل جدید

  1. اضافه کردن به ComputedValueCastEnum
  2. پیاده‌سازی تبدیل در ComputedValueCastingService
// در ComputedValueCastEnum
case Decimal = 'decimal';

// در ComputedValueCastingService
private function castToDecimal(mixed $data): string
{
return number_format((float) $data, 2, '.', '');
}

آداپتور رویداد سفارشی

final class CustomEventAdapter
{
public function adaptToComputedValueEvent($event): ComputedValueEvent
{
return new ComputedValueEvent(...);
}
}

کشف سفارشی

final class CustomDiscovery extends ModelDiscoveryService
{
protected function getModelDirectories(): array
{
$dirs = parent::getModelDirectories();
$dirs[] = app_path('CustomModels');
return $dirs;
}
}

API‌های داخلی

ComputedValueManager

processComputedValueUpdate(
ComputedValueEvent $event,
ComputedValueConfig $config,
string $entityClass
): void

getComputedValueData(
string $entityClass,
int|string $entityId,
string $key
): ?array

clearComputedValueData(
string $entityClass,
int|string $entityId,
string $key
): bool

ComputedValueStorageService

storeComputedValueResults(
string $entityClass,
string $cacheKey,
Collection $results,
ComputedValueConfig $config,
?string $eventTimestamp
): void

getComputedValueData(
string $entityClass,
int|string $entityId,
string $key
): ?array

clearComputedValueData(
string $entityClass,
int|string $entityId,
string $key
): bool

ModelDiscoveryService

discoverModelsWithComputedValues(): array

clearDiscoveryCache(): void

ComputedValueCastingService

castData(mixed $data, ComputedValueCastEnum $cast): mixed

امنیت

  • محافظت از فیلد از دستکاری مستقیم جلوگیری می‌کند
  • اعتبارسنجی روی تمام پیکربندی‌ها
  • ایمنی نوع مبتنی بر enum از پیکربندی‌های نامعتبر جلوگیری می‌کند
  • مکانیسم دور زدن فقط برای سرویس ذخیره‌سازی
  • ردیابی حسابرسی با برچسب‌های زمانی و شناسه‌های همبستگی
  • محافظت از تخصیص انبوه از طریق ویژگی
  • محافظت از کوئری از طریق محدوده سراسری
  • ایزوله‌سازی کنترل‌کننده با تزریق وابستگی
ملاحظات امنیتی

فیلد computed_values در سطوح چندگانه محافظت شده تا یکپارچگی داده تضمین شود. فقط سرویس ذخیره‌سازی می‌تواند از طریق مکانیزم فراخوانی کنترل شده محافظت را دور بزند.

مسائل رایج

کنترل‌کننده یافت نشد

  • بررسی نام‌گذاری: {CacheKey}ComputedValueHandler
  • تأیید مکان با ساختار ماژول مطابقت دارد
  • اطمینان از پیاده‌سازی اینترفیس ComputedValueHandler

مقادیر به‌روز نمی‌شوند

  • بررسی پیاده‌سازی HasComputedValues توسط مدل
  • تأیید وجود ستون computed_values
  • اجرای کشف: php artisan computed-values:discover
  • بررسی لاگ‌ها برای خطاها

هشدارهای شرایط رقابتی

رفتار عادی - نشان می‌دهد داده جدیدتری وجود دارد، از به‌روزرسانی‌های قدیمی جلوگیری می‌کند.


نویسنده: بهنام مرادی
نسخه: 1.0
آخرین به‌روزرسانی: دسامبر 2024