إنتقل إلى المحتوى الرئيسي

تركيب السلوك: ما تعلمنا إياه الألعاب عن هندسة البرمجيات

· 5 دقائق قراءة
أسامة الغانمي
المؤسس المشارك والقائد التقني

يجمع اللاعبون في لعبة إرم شظايا مدارية — أجزاء من السلوك تتراكب معًا لخلق قدرات جديدة. جهّز الدفاع والترميم معًا، وتتجدد دروعك أسرع بمقدار 1.5 ضعف. جهّز التعطيل والتصنيع، وتُلحق فخاخك ضررًا في منطقة.

هذه ليست مجرد آلية لعب. إنها pattern (نمط معماري) في هندسة البرمجيات يحل الجدل بين الـ microservices (خدمات مصغرة مستقلة) والـ monolith (تطبيق متجانس في قاعدة كود واحدة).

مشكلة التركيب

تعلق هندسة البرمجيات بين خيارين سيئين:

الـ monolith: كل شيء في قاعدة كود واحدة. سهل البناء، مستحيل التوسعة. يخاطر كل تغيير بكسر شيء غير متعلق.

الـ microservices: كل شيء في خدمته الخاصة. سهل التوسعة، مستحيل التنسيق. تتطلب كل ميزة تنظيم 5 خدمات و3 طوابير رسائل ودعاء.

يعامل كلا النهجين السلوك كمشكلة موقع: أين يعيش الكود؟

السؤال الحقيقي هو: كيف يتركب السلوك؟

دروس من تصميم الألعاب

تُحدَّد شخصية اللاعب في إرم، لعبة أكشن RPG للزنزانات مبنية على Almadar، من خلال أي الـ orbitals (الوحدات المدارية التي تُركّب السلوك) يُجهّزها:

الـ orbitalالسلوك
الدفاعامتصاص الضرر، توليد الدروع
الترميمالشفاء بمرور الوقت، إزالة effects الحالة
التعطيلمقاطعة الأعداء، تطبيق الإضعافات
التصنيعإنشاء الفخاخ، بناء الأبراج
الاستكشافكشف الخريطة، اكتشاف الأعداء المخفيين
التحويلتحويل الموارد، ترقية المعدات
القيادةتقوية الحلفاء، تنسيق الإجراءات الجماعية
الأرشفةتسجيل patterns الأعداء، كشف نقاط الضعف

كل orbital هو state machine (آلة حالة تدير السلوك عبر حالات محددة) مكتفية ذاتيًا. لا يعرف الدفاع عن الترميم. ولا يعرف الاستكشاف عن التصنيع.

لكن عندما تُجهّزها معًا، يظهر emergent behavior (سلوك ناشئ من تركيب الأجزاء).

الـ resonance: التركيب يخلق الظهور

تخلق الـ orbitals المتوافقة resonance (رنين — تأثيرات تآزرية بين الـ orbitals) عندما تُجهَّز في وقت واحد — effects لا يُعرّفها أي orbital بمفرده:

{
"resonance": [
{
"requires": ["Defend", "Mend"],
"effect": "Shield regeneration rate increased by 1.5x",
"multiplier": { "shieldRegen": 1.5 }
},
{
"requires": ["Disrupt", "Fabricate"],
"effect": "Traps apply disruption debuffs",
"multiplier": { "trapDamage": 1.3 }
},
{
"requires": ["Archive", "Command"],
"effect": "Allies receive enemy weakness intel",
"multiplier": { "allyDamage": 1.2 }
}
]
}

الرؤية الجوهرية: لا يتغير أي orbital. لا يملك الدفاع كودًا لـ "اعمل بشكل أفضل مع الترميم." يعتبر الـ resonance خاصية التركيبة، وليس الأفراد.

هذا بالضبط كيف يجب أن يعمل تركيب البرمجيات.

الـ pattern: تركيب الـ orbital

تتواصل الـ orbitals في Almadar عبر الأحداث. يُصرّح كل orbital بما يُطلقه وما يستمع إليه:

{
"name": "DefendOrbital",
"traits": [{
"name": "ShieldTrait",
"emits": ["SHIELD_ACTIVATED", "SHIELD_DEPLETED"],
"stateMachine": {
"states": [
{ "name": "Ready", "isInitial": true },
{ "name": "Active" },
{ "name": "Cooldown" }
],
"transitions": [
{
"from": "Ready",
"to": "Active",
"event": "ACTIVATE_SHIELD",
"effects": [
["set", "@entity.shieldHp", "@entity.maxShieldHp"],
["emit", "SHIELD_ACTIVATED"]
]
},
{
"from": "Active",
"to": "Cooldown",
"event": "SHIELD_BROKEN",
"effects": [
["set", "@entity.shieldHp", 0],
["emit", "SHIELD_DEPLETED"]
]
}
]
}
}]
}
{
"name": "MendOrbital",
"traits": [{
"name": "HealTrait",
"listens": [
{ "event": "SHIELD_DEPLETED", "triggers": "EMERGENCY_HEAL" }
],
"stateMachine": {
"transitions": [
{
"from": "Idle",
"to": "Healing",
"event": "EMERGENCY_HEAL",
"effects": [
["set", "@entity.hp", ["+", "@entity.hp", ["*", "@entity.maxHp", 0.2]]]
]
}
]
}
}]
}

يُطلق الدفاع SHIELD_DEPLETED. ويستمع الترميم إليه. يبدأ الشفاء تلقائيًا عندما يُكسر الدرع. لا يشير أي orbital إلى الآخر بالاسم. يتواصلان عبر الـ event bus (ناقل الأحداث الذي يربط الـ orbitals ببعضها).

يعني هذا:

  • ترابط فضفاض — الدفاع يعمل بدون الترميم
  • قابلية التركيب — أضف الترميم ويظهر سلوك جديد
  • قابلية الـ validation (التحقق من صحة التوصيل) — الـ compiler (المُصرِّف) يتحقق أن كل emit له listen
  • قابلية الاكتشاف — اقرأ تصريحات الأحداث لفهم التفاعلات

الآثار على هندسة البرمجيات

يترجم هذا الـ pattern مباشرة إلى برمجيات الأعمال:

التجارة الإلكترونية: معالجة الطلبات

OrderOrbital         PaymentOrbital         InventoryOrbital
│ │ │
├─ emits: ├─ listens: ├─ listens:
│ ORDER_PLACED │ ORDER_PLACED │ PAYMENT_CONFIRMED
│ │ → PROCESS_PAYMENT │ → RESERVE_STOCK
│ │ │
│ ├─ emits: ├─ emits:
│ │ PAYMENT_CONFIRMED │ STOCK_RESERVED
│ │ PAYMENT_FAILED │ OUT_OF_STOCK

ثلاثة orbitals. كل منها مكتفٍ ذاتيًا. التركيب عبر الأحداث. يتحقق الـ compiler من اكتمال رسم الأحداث — لا تمر رسالة بدون معالجة.

قارن هذا بنسخة الـ microservices:

  • ثلاث خدمات، ثلاث عمليات نشر، ثلاث قواعد بيانات
  • طابور رسائل (Kafka/RabbitMQ) لربطها
  • طوابير الرسائل الميتة للرسائل الفاشلة
  • patterns الملحمة للمعاملات الموزعة
  • مراقبة وتنبيهات لكل خدمة

تُصرَّف نسخة Almadar إلى نشر واحد بنفس البنية المدفوعة بالأحداث، لكن بدون أعباء البنية التحتية.

التعاون الجماعي: التطوير المتوازي

يمكن للفرق العمل بالتوازي لأن الـ orbitals تتواصل فقط عبر الأحداث:

  • يبني الفريق أ orbital الطلب
  • يبني الفريق ب orbital الدفع
  • يبني الفريق ج orbital المخزون

يتفقون على عقود الأحداث:

{
"event": "ORDER_PLACED",
"payload": {
"orderId": "string",
"items": "array",
"total": "number"
}
}

ثم يبنون بشكل مستقل. يتحقق الـ compiler من تطابق العقود عندما تُركَّب الـ orbitals.

الـ standard library: سلوكيات جاهزة

يتضمن Almadar 11 سلوكًا في الـ standard library (المكتبة القياسية التي توفر سلوكيات جاهزة) تُدمج في أي مشروع:

السلوكماذا يفعل
std/Loadingحالات التحميل مع معالجة النجاح/الخطأ
std/Fetchجلب بيانات غير متزامن مع إعادة المحاولة
std/Submitإرسال النماذج مع الـ validation
std/Retryمنطق إعادة المحاولة بتراجع أسي
std/Pollpatterns الاستطلاع الطويل
std/Paginationترقيم الصفحات بمؤشر/إزاحة
std/Searchبحث نص كامل مع تصفية
std/Sortفرز متعدد المفاتيح
std/GameCoreحلقة اللعبة الأساسية (تحديث، عرض)
std/UnitBehaviorسلوكيات الوحدات الذكية (دورية، حراسة، هروب)
std/Inventoryإدارة مخزون اللعبة

استوردها في أي orbital:

{
"uses": [{ "from": "std/Pagination", "as": "Paginate" }],
"traits": [
{ "ref": "Paginate.traits.PaginationTrait" },
{ "ref": "TaskInteraction" }
]
}

تملك قائمة المهام الخاصة بك الآن ترقيم صفحات. بدون كتابة كود. مجرد تركيب.

لماذا تفهم الألعاب هذا بشكل صحيح

فهمت الألعاب التركيب منذ الأزل. شخصية RPG هي تركيب من:

  • الفئة (محارب، ساحر، متسلل)
  • المعدات (سيف، درع، عصا)
  • المهارات (كرة نار، شفاء، تسلل)
  • التقويات/الإضعافات (مسموم، مبارك، مُسرَّع)

يعتبر كل منها سلوكًا مكتفيًا ذاتيًا. ويخلقون معًا شخصية فريدة ذات قدرات ناشئة.

يجب أن تعمل البرمجيات التجارية بنفس الطريقة:

  • تعتبر الفاتورة تركيبًا من عمليات CRUD + الموافقة + توليد PDF + إشعار بالبريد الإلكتروني
  • يعتبر المستخدم تركيبًا من المصادقة + الملف الشخصي + التفضيلات + سجل النشاط
  • تعتبر لوحة المعلومات تركيبًا من المخططات + الفلاتر + التحديثات الفورية + التصدير

كل سلوك هو orbital. التركيب عبر الأحداث. يضمن الـ compiler صحة التوصيل.

الخلاصة

يسأل الجدل بين الـ microservices والـ monolith السؤال الخاطئ. لا يكمن السؤال في أين يعيش السلوك. بل كيف يتركب السلوك.

تمنحك الـ orbitals:

  • وحدات مكتفية ذاتيًا (كالـ microservices) — كل orbital يملك الـ state machine الخاصة به
  • تركيب سهل (كالـ monolith) — استورد، ركِّب، صرِّف
  • توصيل مُتحقق منه (لا يوفره أي منهما) — الـ compiler يتحقق من كل اتصال حدث
  • emergent behavior (كالألعاب) — effects من الـ resonance بين التركيبات المتوافقة

لا تبدأ بـ "كم عدد الخدمات؟" في المرة القادمة التي تصمم فيها نظامًا. بل ابدأ بـ "ما السلوكيات التي أحتاجها، وكيف تتركب؟"

تعرف على المزيد حول الأحداث عبر الـ orbitals والـ standard library.

أحدث المنشورات