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

بهترین شیوه‌های سیستم کش

این راهنما شیوه‌های توصیه شده برای استفاده مؤثر از سیستم کش در اپلیکیشن شما را تشریح می‌کند.

نام‌گذاری کلیدهای کش

قراردادهای نام‌گذاری

این قراردادهای نام‌گذاری را برای کلیدهای کش دنبال کنید:

// ✅ خوب: توصیفی و سازگار
'user_profile_data'
'product_recommendations'
'order_statistics'
'category_hierarchy'

// ❌ بد: مبهم یا ناسازگار
'data'
'info'
'userdata' // ناسازگار با الگوی snake_case
'ProductInfo' // ناسازگار با الگوی snake_case

فضای نام بر اساس ماژول

کلیدهای کش را با نام ماژول پیشوند دهید تا از تداخل جلوگیری کنید:

// ✅ خوب: فضای نام بر اساس ماژول
'user_profile_data'
'user_activity_metrics'
'product_related_items'
'product_pricing_tiers'

// ❌ بد: بدون زمینه ماژول
'profile'
'metrics'
'related'
'pricing'

نسخه‌گذاری

هنگام ایجاد تغییرات شکننده، اطلاعات نسخه را در کلیدهای کش قرار دهید:

// ✅ خوب: نسخه در کلید گنجانده شده
'user_profile_data_v2'
'product_recommendations_v3'

// رویکرد جایگزین: نسخه در فراداده
$resultData = [
'data' => $transformedData,
'metadata' => [
'version' => '2.0',
'last_updated' => now()->toIso8601String(),
]
];

پیکربندی کش

انتخاب رویداد

در مورد رویدادهایی که باید به‌روزرسانی کش را راه‌اندازی کنند، مشخص باشید:

// ✅ خوب: رویدادهای مشخص
new CacheConfigDTO(
key: 'product_recommendations',
relatedEvents: [
ProductUpdatedEvent::class,
ProductCategoryChangedEvent::class,
ProductTagsChangedEvent::class,
],
// سایر پیکربندی‌ها...
)

// ❌ بد: خیلی گسترده یا رویدادهای مهم گم شده
new CacheConfigDTO(
key: 'product_recommendations',
relatedEvents: [
ProductUpdatedEvent::class, // رویدادهای دسته‌بندی و برچسب گم شده
],
// سایر پیکربندی‌ها...
)

انتخاب حالت پردازش

حالت پردازش مناسب را بر اساس ماهیت کش انتخاب کنید:

@tab کش همزمان

// از حالت SYNC استفاده کنید وقتی:
// - کش برای تجربه فوری کاربر حیاتی است
// - تولید کش سریع است (< 100ms)
// - سازگاری داده‌ها حیاتی است

new CacheConfigDTO(
key: 'product_basic_info',
relatedEvents: [ProductUpdatedEvent::class],
sourceModule: 'Product',
sourceEntity: Product::class,
mode: CacheModeEnum::SYNC, // به‌روزرسانی فوری
cast: CacheCastEnum::OBJECT,
)

@tab کش ناهمزمان

// از حالت ASYNC استفاده کنید وقتی:
// - تولید کش از نظر محاسباتی پرهزینه است
// - کش برای تجربه فوری کاربر حیاتی نیست
// - تأخیر در به‌روزرسانی کش قابل قبول است

new CacheConfigDTO(
key: 'product_recommendations',
relatedEvents: [ProductViewedEvent::class],
sourceModule: 'Recommendations',
sourceEntity: Product::class,
mode: CacheModeEnum::ASYNC, // پردازش در پس‌زمینه
cast: CacheCastEnum::ARRAY,
)

انتخاب نوع تبدیل (Cast)

مناسب‌ترین نوع تبدیل را برای داده‌های خود انتخاب کنید:

// ✅ خوب: انواع تبدیل مناسب
new CacheConfigDTO(
key: 'is_featured',
// سایر پیکربندی‌ها...
cast: CacheCastEnum::BOOLEAN, // برای پرچم‌های بولی
)

new CacheConfigDTO(
key: 'view_count',
// سایر پیکربندی‌ها...
cast: CacheCastEnum::INTEGER, // برای شمارش‌های عددی
)

new CacheConfigDTO(
key: 'last_activity',
// سایر پیکربندی‌ها...
cast: CacheCastEnum::DATETIME, // برای تاریخ و زمان
)

// ❌ بد: انواع تبدیل نامناسب
new CacheConfigDTO(
key: 'is_featured',
// سایر پیکربندی‌ها...
cast: CacheCastEnum::STRING, // باید BOOLEAN باشد
)

پیاده‌سازی کنترل‌کننده کش

مسئولیت واحد

هر کنترل‌کننده کش باید بر یک کلید کش واحد متمرکز باشد:

// ✅ خوب: مسئولیت واحد
final readonly class RelatedProductsCacheHandler implements CacheHandler
{
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
// فقط کلید کش 'related_products' را مدیریت می‌کند
}
}

// ❌ بد: مسئولیت‌های متعدد
final readonly class ProductCacheHandler implements CacheHandler
{
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
// چندین کلید کش را مدیریت می‌کند
if ($eventDto->getCacheKey() === 'related_products') {
// مدیریت محصولات مرتبط
} elseif ($eventDto->getCacheKey() === 'product_statistics') {
// مدیریت آمار
}
}
}

مدیریت خطا

مدیریت خطای قوی در کنترل‌کننده‌های کش پیاده‌سازی کنید:

// ✅ خوب: مدیریت خطای جامع
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
try {
$productId = $eventDto->getEntityId();
$product = Product::find($productId);

if (!$product) {
$this->logger->warning('محصول یافت نشد', [
'product_id' => $productId,
'cache_key' => 'related_products'
]);
return new CacheHandlerResultCollection();
}

// پردازش داده کش
$relatedProducts = $this->getRelatedProducts($product);

return new CacheHandlerResultCollection([
new CacheHandlerResult($productId, $relatedProducts)
]);
} catch (Exception $e) {
$this->logger->error('تولید کش شکست خورد', [
'product_id' => $productId ?? null,
'cache_key' => 'related_products',
'exception' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);

// در صورت خطا مجموعه خالی برگردان
return new CacheHandlerResultCollection();
}
}

ساختار داده

فرمت سازگار

ساختار داده سازگار در کنترل‌کننده‌های کش حفظ کنید:

// ✅ خوب: ساختار سازگار با فراداده
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
// پردازش داده...

$result = [
'data' => $processedData,
'metadata' => [
'count' => count($processedData),
'generated_at' => now()->toIso8601String(),
'source' => 'ProductModule',
'version' => '1.0',
]
];

return new CacheHandlerResultCollection([
new CacheHandlerResult($entityId, $result)
]);
}

الگوهای دسترسی کش

تنزل تدریجی (Graceful Degradation)

هنگام گم شدن داده‌های کش، تنزل تدریجی پیاده‌سازی کنید:

// ✅ خوب: تنزل تدریجی
public function getProductWithRelated(int $productId)
{
$product = Product::find($productId);

if (!$product) {
return null;
}

// بررسی وجود کش
if (!$product->hasCacheData('related_products')) {
// راه‌اندازی تولید کش
event(new ProductCacheRequested($product));

// فعلاً آرایه خالی برگردان
$relatedProducts = [];
} else {
$relatedProducts = $product->related_products;
}

return [
'product' => $product,
'related_products' => $relatedProducts,
];
}

دسترسی دفاعی

هنگام دسترسی به داده‌های کش از برنامه‌نویسی دفاعی استفاده کنید:

// ✅ خوب: دسترسی دفاعی
public function getUserStats(User $user)
{
// بررسی وجود کش
if (!$user->hasCacheData('activity_metrics')) {
return [
'post_count' => 0,
'comment_count' => 0,
'like_count' => 0,
];
}

$metrics = $user->activity_metrics;

return [
'post_count' => $metrics['post_count'] ?? 0,
'comment_count' => $metrics['comment_count'] ?? 0,
'like_count' => $metrics['like_count'] ?? 0,
];
}

تست

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

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

// ✅ خوب: تست واحد جامع
public function test_related_products_cache_handler()
{
// آماده‌سازی
$product = Product::factory()->create();
$relatedProducts = Product::factory()->count(3)->create([
'category_id' => $product->category_id
]);

$eventDto = new CacheEventDTO(
entityId: $product->id,
event: new ProductUpdatedEvent($product),
cacheKey: 'related_products'
);

$handler = app(RelatedProductsCacheHandler::class);

// عمل
$result = $handler->handle($eventDto);

// بررسی
$this->assertInstanceOf(CacheHandlerResultCollection::class, $result);
$this->assertCount(1, $result);
$this->assertEquals($product->id, $result->first()->getEntityId());

$data = $result->first()->getData();
$this->assertIsArray($data);
$this->assertArrayHasKey('data', $data);
$this->assertCount(3, $data['data']);
}

امنیت

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

قبل از کش کردن، داده‌های حساس را فیلتر کنید:

// ✅ خوب: فیلتر کردن داده‌های حساس
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
$userId = $eventDto->getEntityId();
$user = User::find($userId);

if (!$user) {
return new CacheHandlerResultCollection();
}

// فیلتر کردن داده‌های حساس
$profileData = [
'name' => $user->name,
'username' => $user->username,
'avatar' => $user->avatar_url,
'bio' => $user->bio,
// ایمیل، رمز عبور و غیره را شامل نکنید
];

return new CacheHandlerResultCollection([
new CacheHandlerResult($userId, $profileData)
]);
}

عملکرد

به‌روزرسانی‌های انتخابی

فقط آنچه ضروری است به‌روزرسانی کنید:

// ✅ خوب: به‌روزرسانی‌های انتخابی
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
$event = $eventDto->getEvent();
$productId = $eventDto->getEntityId();

// فقط در صورت تغییر قیمت، کش مربوط به قیمت را به‌روزرسانی کن
if ($event instanceof ProductPriceChangedEvent) {
return $this->handlePriceChange($productId);
}

// فقط در صورت تغییر موجودی، کش مربوط به موجودی را به‌روزرسانی کن
if ($event instanceof ProductStockChangedEvent) {
return $this->handleStockChange($productId);
}

// به‌روزرسانی کامل برای سایر رویدادها
return $this->handleFullUpdate($productId);
}

نظارت و نگهداری

آمار کش

نظارت بر آمار کش پیاده‌سازی کنید:

// ✅ خوب: نظارت بر آمار کش
public function getCacheHealthReport()
{
$products = Product::take(100)->get();

$stats = [
'total_products' => $products->count(),
'products_with_complete_cache' => 0,
'products_with_partial_cache' => 0,
'products_without_cache' => 0,
'cache_keys' => [
'related_products' => 0,
'pricing_tiers' => 0,
'stock_status' => 0,
],
];

foreach ($products as $product) {
$productStats = $product->getCacheStatistics();

if ($productStats['cached_keys'] === $productStats['total_keys']) {
$stats['products_with_complete_cache']++;
} elseif ($productStats['cached_keys'] === 0) {
$stats['products_without_cache']++;
} else {
$stats['products_with_partial_cache']++;
}

foreach ($productStats['keys_with_data'] as $key) {
if (isset($stats['cache_keys'][$key])) {
$stats['cache_keys'][$key]++;
}
}
}

return $stats;
}

الگوهای پیشرفته

نسخه‌گذاری کش

برای تغییرات شکننده، نسخه‌گذاری کش پیاده‌سازی کنید:

// ✅ خوب: نسخه‌گذاری کش
public function handle(CacheEventDTO $eventDto): CacheHandlerResultCollection
{
$productId = $eventDto->getEntityId();
$product = Product::find($productId);

if (!$product) {
return new CacheHandlerResultCollection();
}

$data = $this->generateCacheData($product);

// اضافه کردن اطلاعات نسخه
$result = [
'data' => $data,
'metadata' => [
'version' => '2.0', // هنگام تغییر ساختار افزایش دهید
'generated_at' => now()->toIso8601String(),
]
];

return new CacheHandlerResultCollection([
new CacheHandlerResult($productId, $result)
]);
}

// در کد مصرف‌کننده
public function getProductData(Product $product)
{
if (!$product->hasCacheData('product_data')) {
return null;
}

$cache = $product->product_data;

// بررسی نسخه و مدیریت متناسب
$version = $cache['metadata']['version'] ?? '1.0';

if ($version === '1.0') {
// مدیریت فرمت قدیمی
return $this->transformLegacyFormat($cache['data']);
}

// نسخه فعلی
return $cache['data'];
}

مراحل بعدی

پس از پیاده‌سازی این بهترین شیوه‌ها، موارد زیر را در نظر بگیرید:

  1. عیب‌یابی - حل مسائل رایج
  2. راهنمای پیاده‌سازی - بررسی جزئیات پیاده‌سازی
  3. محافظت فیلد - درباره محافظت فیلد کش بیاموزید