كيف يعمل EntityFlow
بُني EntityFlow حول مجموعة صغيرة من القرارات المعمارية التي تحافظ على سلوكه حتميًا وقابلًا للاختبار ومتطابقًا عبر تطبيق الويب والخادم وإصدار سطح المكتب.
الفصل بين النواة والغلاف
تقيم كل المنطق الحتمي في حزم TypeScript بلا واجهة (headless) ومستقلة عن أي إطار عمل. أما تطبيق React وخادم Node وتطبيق Electron فهي أغلفة رقيقة تستهلك تلك الحزم — ولا تحتوي على أي منطق أعمال خاص بها.
تغطي الحزم بلا واجهة ما يلي:
- IR المخطط (التمثيل الوسيط)،
- نموذج مستند CRDT،
- الهندسة وتوجيه الحواف،
- توليد شيفرة SQL/DBML عبر أربع لهجات،
- التحقق،
- الإسقاط (نموذج القراءة)،
- مُصيّر اللوحة (canvas renderer)،
- التفويض.
ولأن المنطق مشترك، فإن ما تراه في المتصفح وما يُصدّره الخادم وما يُنتجه تطبيق سطح المكتب يأتي كله من الشيفرة نفسها.
الاستمرارية المزدوجة (CQRS)
يستخدم EntityFlow فصلًا بين الأمر والاستعلام مع تمثيلين لكل مخطط:
- نموذج الكتابة المرجعي هو Yjs CRDT، يُخزَّن ككتلة ثنائية (blob). كل تعديل يُغيّر هذه الكتلة.
- يُشتق إسقاط قراءة علائقي من الكتلة لأغراض الاستعلامات والبحث والتصدير من جانب الخادم.
يكون التدفق باتجاه واحد بصرامة:
CRDT blob → IR → projection rows
لا تُعاد كتابة الإسقاط في الكتلة أبدًا. وإذا انحرف صف من الإسقاط أو فشل في أي وقت، فإنه ببساطة يُعاد اشتقاقه من الكتلة — فالكتلة هي المصدر الوحيد للحقيقة.
الزمن الفعلي
يعمل خادم websocket Hocuspocus في العملية ذاتها من Node التي تعمل بها API، ويزامن CRDT بين المحرِّرين المتصلين. أما الحضور المباشر (المؤشرات والتحديدات ومن المتصل) فينتقل عبر awareness الخاص بـ Yjs، والذي لا يُحفظ أبدًا — فهو موجود فقط طوال مدة الاتصال.
ولأن التصدير من جانب الخادم يصدر عن IR المشتقة من الكتلة، فإن SQL/DBML الذي يُنتجه مطابق بايتًا ببايت لما يصدّره المحرر داخل المتصفح.
التوسّع الأفقي
تتعامل العقدة الواحدة بمفردها مع حِمل عمل كبير. ولتشغيل أكثر من نسخة خادم واحدة، فعّل توزيع Redis (fan-out) حتى ينتشر الحضور وتحديثات المستندات عبر الـ pods. وبدونه تتباعد النسخ. راجع التوسّع والأداء للحصول على التفاصيل وأهداف SLO.