أنماط الهندسة المعمارية وتصميم الأنظمة
أنماط الهندسة المعمارية المدفوعة بالأحداث
الهندسة المعمارية المدفوعة بالأحداث (EDA) تمكّن الاقتران الفضفاض، القابلية للتوسع، والمعالجة في الوقت الحقيقي. هذه الأنماط ضرورية للهندسات السحابية الحديثة.
أنواع الأحداث ودلالاتها
تصنيف الأحداث
| النوع | الغرض | مثال |
|---|---|---|
| أحداث المجال | حقائق الأعمال | OrderPlaced، PaymentReceived |
| أحداث التكامل | الاتصال عبر الخدمات | CustomerCreatedEvent |
| أحداث النظام | تغييرات البنية التحتية | InstanceTerminated، QueueFull |
بنية الحدث
{
"eventId": "uuid-v4",
"eventType": "OrderPlaced",
"timestamp": "2026-01-05T10:30:00Z",
"version": "1.0",
"source": "order-service",
"data": {
"orderId": "12345",
"customerId": "67890",
"totalAmount": 99.99
},
"metadata": {
"correlationId": "request-uuid",
"causationId": "previous-event-uuid"
}
}
سؤال المقابلة: دقة الأحداث
س: "هل يجب أن نصدر أحداثاً دقيقة (OrderItemAdded) أو أحداثاً خشنة (OrderUpdated)؟"
ج: اعتبر المقايضات:
| النهج | الإيجابيات | السلبيات |
|---|---|---|
| دقيق | ردود أفعال محددة، سجل مراجعة | أحداث أكثر، مستهلكون معقدون |
| خشن | أبسط، أحداث أقل | قد يُخطر بشكل مفرط، سياق أقل |
| هجين | أفضل ما في الاثنين | تعقيد أكثر للإدارة |
التوصية: ابدأ بالخشن، أضف الدقيق عندما تظهر احتياجات مستهلك محددة.
توريد الأحداث (Event Sourcing)
المفهوم
تخزين الحالة كتسلسل من الأحداث بدلاً من الحالة الحالية.
التقليدي:
جدول المستخدم: { id: 1, name: "John", email: "john@example.com" }
توريد الأحداث:
الأحداث:
1. UserCreated { id: 1, name: "John" }
2. EmailUpdated { id: 1, email: "john@example.com" }
3. NameChanged { id: 1, name: "John Doe" }
الفوائد والمقايضات
| الفائدة | المقايضة |
|---|---|
| سجل مراجعة كامل | نمو التخزين |
| الاستعلامات الزمنية ("الحالة في الوقت X") | نماذج قراءة معقدة |
| إعادة تشغيل الأحداث للتصحيح | الاتساق النهائي |
| ملاءمة طبيعية لـ CQRS | منحنى التعلم |
سؤال المقابلة: متى تستخدم توريد الأحداث
س: "شركة خدمات مالية تريد تنفيذ توريد الأحداث لجميع الخدمات. انصحهم."
ج: توريد الأحداث ليس مناسباً في كل مكان:
مناسب:
- متطلبات المراجعة (المعاملات المالية، الامتثال)
- مجال معقد مع قواعد أعمال غنية
- الحاجة للاستعلامات الزمنية
- تكاملات مدفوعة بالأحداث موجودة بالفعل
غير مناسب:
- تطبيقات CRUD البسيطة
- متطلبات تحليلات في الوقت الحقيقي
- الفريق غير مألوف مع النمط
- لا متطلبات مراجعة
التوصية: استخدم توريد الأحداث للمعاملات المالية الأساسية (دفتر الأستاذ، الصفقات). استخدم الاستمرارية التقليدية للخدمات الداعمة (إدارة المستخدمين، الإشعارات).
CQRS: فصل مسؤولية الأمر والاستعلام
الهندسة المعمارية
فصل نماذج القراءة والكتابة:
الأوامر (الكتابة):
العميل → معالج الأمر → نموذج المجال → مخزن الأحداث
↓
الأحداث منشورة
↓
الاستعلامات (القراءة):
العميل → معالج الاستعلام → نموذج القراءة (عروض غير مُطبَّعة)
↑
عارض الأحداث
فوائد CQRS
| الفائدة | الشرح |
|---|---|
| قراءات قابلة للتوسع | نموذج القراءة محسّن للاستعلامات |
| كتابات محسّنة | نموذج الكتابة مركز على منطق الأعمال |
| المرونة | تخزين مختلف للقراءة/الكتابة |
| الأداء | عروض مادية للاستعلامات المعقدة |
سؤال المقابلة: اتساق CQRS
س: "كيف تتعامل مع فجوة الاتساق النهائي في CQRS؟"
ج: استراتيجيات لإدارة الاتساق:
- تحديثات UI متفائلة: أظهر الحالة المعلقة فوراً
- Polling/WebSockets: حدّث UI عندما يلحق العرض
- معرفات الارتباط: تتبع الأمر حتى نموذج القراءة
- اقرأ-ما-تكتب: وجّه قراءات المستخدم للأساسي أثناء الفجوة
// مثال التحديث المتفائل
async function placeOrder(order) {
// أظهر المعلق في UI فوراً
updateUI({ ...order, status: 'pending' });
// أرسل الأمر
const commandId = await sendCommand('PlaceOrder', order);
// استطلع للتأكيد
await pollUntilProjected(commandId);
// حدّث UI بالحالة المؤكدة
const confirmed = await fetchOrder(order.id);
updateUI(confirmed);
}
منصات بث الأحداث
هندسة Apache Kafka
المنتجون → المواضيع (مقسمة) → مجموعات المستهلكين
↓
الأقسام: [P0] [P1] [P2]
↓
مجموعة المستهلكين: [C0] [C1] [C2]
المفاهيم الرئيسية:
- المواضيع: فئة الأحداث
- الأقسام: وحدة التوازي، مرتبة ضمن القسم
- مجموعات المستهلكين: موازنة الحمل عبر المستهلكين
- الإزاحات: تتبع الموقع لإعادة التشغيل
AWS Kinesis مقابل Kafka
| الميزة | Kinesis | Kafka (MSK) |
|---|---|---|
| الإدارة | بدون خادم | كتل مُدارة |
| التوسع | قائم على الشظايا | قائم على الأقسام |
| الاحتفاظ | 24 ساعة-365 يوم | غير محدود |
| التكامل | AWS أصلي | نظام بيئي أوسع |
| نموذج التكلفة | لكل ساعة شظية + بيانات | قائم على المثيلات |
| الأفضل لـ | AWS أصلي، حمل متغير | إنتاجية عالية، احتفاظ طويل |
سؤال المقابلة: استراتيجية قسم Kafka
س: "أنت تصمم نظام إشعارات لـ 100 مليون مستخدم. كيف تقسم موضوع Kafka؟"
ج: صمم للتوازي والترتيب:
الموضوع: user-notifications
مفتاح القسم: user_id
المنطق:
- جميع إشعارات المستخدم تذهب لنفس القسم
- يضمن الترتيب لكل مستخدم
- 100 قسم للتوازي
- كل قسم ~1 مليون مستخدم في المتوسط
اعتبارات التوسع:
- يمكن إضافة أقسام (يزيد التوازي فقط)
- لا يمكن تقليل الأقسام
- إعادة التوازن تحدث تلقائياً
- اعتبر المستخدمين الساخنين (المشاهير) - قد يحتاجون معالجة خاصة
ضمانات تسليم الأحداث
دلالات التسليم
| الضمان | التعريف | حالة الاستخدام |
|---|---|---|
| مرة واحدة كحد أقصى | قد تفقد الأحداث | إشعارات غير حرجة |
| مرة واحدة على الأقل | قد تتكرر | معظم الحالات (مع التساوي) |
| مرة واحدة بالضبط | لا فقدان، لا تكرار | المعاملات المالية |
تنفيذ التساوي
def process_event(event):
# تحقق إذا تمت المعالجة بالفعل
if event_store.exists(event.id):
return # تخطى التكرار
# معالجة الحدث
result = handle_event(event)
# وضع علامة معالجة (ذرياً مع الآثار الجانبية)
event_store.mark_processed(event.id, result)
سؤال المقابلة: الأحداث المكررة
س: "خدمة الدفع الخاصة بك تستقبل أحداث OrderPlaced مكررة. كيف تمنع الشحن المزدوج؟"
ج: نفذ التساوي على مستويات متعددة:
- مفتاح التساوي: استخدم orderId كمفتاح طبيعي
- جدول إزالة التكرار: تتبع الأحداث المعالجة
- بوابة الدفع: معظمها يدعم مفاتيح التساوي
- قيود قاعدة البيانات: قيد فريد على علاقة الطلب-الدفع
-- منع المدفوعات المكررة
CREATE TABLE payments (
id UUID PRIMARY KEY,
order_id UUID UNIQUE, -- دفعة واحدة فقط لكل طلب
amount DECIMAL,
created_at TIMESTAMP
);
المبدأ الرئيسي: افترض أن الأحداث ستتكرر. صمم جميع المستهلكين ليكونوا متساوين.
بعد ذلك، سنستكشف أنماط التوافر العالي والتعافي من الكوارث. :::