مصادر الحدث: الأساسيات
ما هو مصدر الأحداث
Event Sourcing (ES) هي طريقة لتخزين حالة كائنات المجال ليس كـ «خط حالي»، ولكن كسجل حدث غير قابل للتغيير يصف كل ما حدث. يتم الحصول على الحالة الحالية للمجموع عن طريق لف (إعادة) أحداثه، ويتم بناء أي مشاهدات مقروءة كإسقاطات فوق هذا السجل.
الآثار الرئيسية:- التاريخ هو «المصدر الأساسي للحقيقة»، الدولة هي إسقاط التاريخ.
- يمكن تكرار أي حالة والتحقق منها وشرحها (التدقيق).
- لا تتطلب إضافة وجهات نظر وتحليلات جديدة هجرات من «اللقطات» القديمة - بل يكفي خسارة الأحداث.
الشروط الأساسية
المجموع - وحدة اتساق المجال مع الثوابت الواضحة (الطلب، الدفع، رصيد المستخدم).
الحدث - حقيقة ثابتة حدثت في الماضي (الدفع. مأذون به '،' أمر. شحنها ').
Event Store هو سجل ملحق فقط يوفر ترتيب الأحداث داخل الوحدة.
النسخة الإجمالية هي رقم آخر حدث مطبق (للتزامن المتفائل).
لقطة - انطباع دوري عن الدولة لتسريع الالتفاف.
الإسقاط (نموذج القراءة) - عرض ملموس للقراءة/البحث/الإبلاغ (غالبًا ما يكون غير متزامن).
كيف يعمل (خيط → الأحداث → الإسقاطات)
1. يرسل العميل أمرًا («CapturePayment» و «PlaceOrder»).
2. يقوم المجمع بالتحقق من صحة الثوابت، وإذا كان الجميع على ما يرام، فإنه يولد الأحداث.
3. تتم إضافة الأحداث ذريًا إلى Event Store مع التحقق من الإصدار (تزامن متفائل).
4. تشترك معالجات الإسقاط في تدفق الحدث وتحديث النماذج.
5. عندما يتم تحميل المجمع للأمر التالي، تتم استعادة الحالة إلى لقطة (إن وجدت) → الحدث بعد اللقطة.
تصميم الحدث
الصفات المطلوبة (أساسي)
json
{
"event_id": "uuid",
"event_type": "payment. authorized. v1",
"aggregate_type": "Payment",
"aggregate_id": "pay_123",
"aggregate_version": 5,
"occurred_at": "2025-10-31T10:42:03Z",
"payload": { "amount": 1000, "currency": "EUR", "method": "card" },
"meta": { "trace_id": "t-abc", "actor": "user_42" }
}
التوصيات:
- التسمية: 'المجال. العمل. v {major} '.
- الإضافة: الحقول الجديدة اختيارية، دون تغيير معنى الحقول القديمة.
- الحد الأدنى: الحقائق فقط، دون ازدواجية البيانات التي يمكن استردادها بسهولة.
العقود والمخططات
إصلاح المخططات (Avro/JSON Schema/Protobuf) والتحقق من التوافق في CI.
للتغييرات «الكسر» - نسخة رئيسية جديدة من الحدث والمنشور الموازي «v1 »/« v2» لفترة الهجرة.
الوصول التنافسي: تزامن متفائل
القاعدة: لا يمكن كتابة الأحداث الجديدة إلا إذا كانت 'متوقعة _ نسخة = = current_version'.
Cseudocode:pseudo load: snapshot(state, version), then apply events > version new_events = aggregate. handle(command)
append_to_store(aggregate_id, expected_version=current_version, events=new_events)
//if someone has already written an event between load and append, the operation is rejected -> retray with reload
لذلك نضمن سلامة الثوابت بدون معاملات موزعة.
لقطات (تسارع الالتفاف)
خذ لقطة لكل أحداث N أو مؤقت.
Храните 'snapshot _ state', 'aggregate _ id', 'version', 'created _ at'.
تحقق دائمًا وتابع الأحداث بعد اللقطة (لا تثق فقط في الممثلين).
قم بإزالة اللقطات بحيث يمكن إعادة إنشائها من السجل (لا تخزن الحقول «السحرية»).
التوقعات و CQRS
يتم دمج ES بشكل طبيعي مع CQRS:- نموذج الكتابة = مجاميع + متجر الأحداث.
- اقرأ النماذج = التوقعات المحدثة بواسطة الأحداث (بطاقات Redis، OpenSearch للبحث، ClickHouse/OLAP للإبلاغ).
- التوقعات خفية: إعادة معالجة نفس «event _ id» لا تغير النتيجة.
تطور الدائرة وتوافقها
المادة المضافة أولاً: الحقول المضافة ؛ لا تغير الأنواع/الدلالات.
للتغييرات المعقدة، أطلق أنواعًا جديدة من الأحداث واكتب مهاجر الإسقاط.
احتفظ بإدخال مزدوج («v1» + «v2») للفترة الانتقالية وأطلق النار على «v1» عندما تكون جميع التوقعات جاهزة.
السلامة و PII و «الحق في النسيان»
غالبًا ما يحتوي التاريخ على بيانات حساسة. النهج:- تقليل PII في الأحداث (المعرفات بدلاً من البيانات، التفاصيل في الجوانب المحمية).
- محو التشفير: حقول التشفير، وعندما يُطلب حذفها، تدمير المفتاح (يبقى الحدث، لكن البيانات غير متوفرة).
- أحداث المراجعة: "مستخدم. مجزأة. v1 'مع استبدال المجالات الحساسة في الإسقاطات (يحافظ التاريخ على حقيقة التحرير).
- سياسات الاحتفاظ: بالنسبة لبعض المجالات، يمكن أرشفة بعض الأحداث لتخزين WORM.
الأداء والنطاق
التقسيم: الترتيب مهم داخل المجموع - التقسيم بواسطة «agregate _ id».
البداية الباردة: لقطات + الالتفاف الدوري «المضغوط».
تذييل الدفعة - أحداث جماعية في صفقة واحدة.
ضغط خلفي و DLQ لمعالجات الإسقاط ؛ قياس التأخر (الوقت وعدد الرسائل).
فهرسة متجر الأحداث: الوصول السريع بواسطة «(aggregate_type، aggregate_id)» وبحلول الوقت.
اختبار
اختبارات المواصفات للمجموعات - سيناريو «الأوامر → الأحداث المتوقعة».
اختبارات الإسقاط: تغذية تدفق الحدث والتحقق من الحالة/المؤشرات المجسدة.
اختبارات إعادة التشغيل: إعادة بناء الإسقاطات من الصفر على الحامل - تأكد من تطابق النتيجة.
الفوضى/الكمون: حقن التأخير والأخذ، تحقق من الغباء.
أمثلة على المجالات
1) المدفوعات
الأحداث: الدفع. '،' الدفع. مأذون به '،' دفع. «،» الدفع. مستردة '.
الثوابت: لا يمكنك «القبض» بدون «إذن» ؛ والمبالغ غير سلبية ؛ العملة لم تتغير.
التوقعات: «بطاقة الدفع» (KV)، البحث عن المعاملات (OpenSearch)، الإبلاغ (OLAP).
2) الأوامر (التجارة الإلكترونية)
الأحداث: 'النظام. ',' order. دفعت '،' أمر. معبأة '،' طلب. تم شحنها '،' طلب. سلمت '.
الثوابت: تحولات حالة الرسم البياني للدولة ؛ الإلغاء ممكن قبل «الشحن».
الإسقاطات: قائمة طلبات المستعملين، لوحات جيش تحرير السودان حسب الحالة.
3) الميزانيات العمومية (المالية/iGaming)
الأحداث: التوازن. المودعة '،' الرصيد. «،» الرصيد. الرصيد. معدلة '.
الثابت الصلب: لا يزول التوازن <0 ؛ الأوامر «operation _ id».
تُقرأ العمليات الحرجة مباشرة من المجموع (الاتساق الصارم)، واجهة المستخدم - من الإسقاط (في النهاية).
هيكل متجر الأحداث النموذجي (متغير DB)
الأحداث
'event _ id (PK)'، 'مجموع _ نوع'، 'مجموع _ id'، 'نسخة'، 'حدث _ في'، 'حدث _ نوع'، 'حمولة'، 'ميتا'
الفهرس: «(aggregate_type، aggregate_id، النسخة)».
لقطات
'gregate _ type', 'colgrigate _ id', 'version', 'state', 'created _ at'
الفهرس: «(aggregate_type، aggregate_id)».
consumers_offsets
"consumer _ id"، "event _ id "/" position"، "updated _ at' (للتوقعات والبيع بالتجزئة).
الأسئلة المتكررة (الأسئلة الشائعة)
هل استخدام ES إلزامي في كل مكان ؟
لا ، ليس كذلك ES مفيد عندما يكون التدقيق، والمثبطات المعقدة، وقابلية التكاثر، والتمثيلات المختلفة للبيانات مهمة. بالنسبة لـ CRUD البسيط، هذا زائد عن الحاجة.
ماذا عن طلبات «الحالة الحالية» ؟
إما أن تقرأ من الإسقاط (بسرعة، في نهاية المطاف)، أو من الوحدة (أكثر تكلفة، ولكن بدقة). عادة ما تستخدم العمليات الحرجة المسار الثاني.
هل أحتاج إلى وسيط كافكا/ستريم ؟
متجر الأحداث - مصدر الحقيقة ؛ وسيط ملائم لتوزيع الأحداث على أجهزة العرض والأنظمة الخارجية.
ماذا تفعل بـ «الحق في النسيان» ؟
تقليل PII وتشفير الحقول الحساسة وتطبيق محو/تنقيح التشفير في الإسقاطات.
كيف يمكنني ترحيل البيانات القديمة ؟
اكتب نصًا لتوليد الأحداث بأثر رجعي («إعادة الطراز») أو ابدأ بـ «الحالة كما هي» ونشر الأحداث فقط للتغييرات الجديدة.
Antipatterns
مصدر الحدث «بدافع العادة»: يعقد النظام دون فائدة المجال.
أحداث الدهون: حمولات منتفخة مع PII ومضاعفات - الفرامل ومشاكل الامتثال.
عدم وجود تزامن متفائل: فقدان الثوابت عند السباق.
الإسقاطات غير القابلة للتكرار: لا إعادة تشغيل/لقطات → إصلاحات يدوية.
مراكز السيطرة على الأمراض الخام كأحداث مجال: مخططات DB المسربة والاتصال الصعب.
مزج الأحداث الداخلية والتكامل: نشر «عرض» مستقر بالخارج.
قائمة الإنتاج المرجعية
- يتم تعريف المجاميع والثوابت والأحداث (العناوين والنسخ والمخططات).
- يوفر Event Store الطلب ضمن التزامن الإجمالي والمتفائل.
- تم تضمين اللقطات وخطة إعادة بنائها.
- الإسقاطات خفية، وهناك DLQs ومقاييس التأخير.
- يتم التحقق من صحة المخططات في CI، ويتم توثيق سياسة النسخ.
- تم تقليل PII، والحقول مشفرة، وهناك استراتيجية «نسيان».
- فحص إعادة الإسقاط على مقاعد البدلاء ؛ لديه خطة لاستعادة القدرة على العمل بعد الكوارث.
- لوحات القيادة: سرعة التطبيق، تأخر الإسقاط، أخطاء التطبيق، نسبة إعادة التصوير.
المجموع
يجعل Event Sourcing تاريخ النظام قطعة أثرية من الدرجة الأولى: نلتقط الحقائق ونعيد إنتاج الدولة منها ونبني أي تمثيلات بحرية. هذا يعطي التدقيق ومقاومة التغيير ومرونة التحليلات - خاضعة للانضباط في المخططات والمراقبة التنافسية والعمل الكفء مع البيانات الحساسة.