دعني أصف الـstack الذي دخلته في Talentera في يونيو 2018: SaaS توظيف B2B متعدد المستأجرين يخدم الوزارات الحكومية والعملاء المؤسسيين عبر منطقة MENA، يعمل على نواة TCL/TK على AOL Server 4.x، مع PHP 7 على الجانب للميزات الأحدث، وNode.js لبعض طبقات الـAPI، ومحرك Camunda BPMN لتنسيق سير العمل. هذا لم يكن نموذجاً أولياً. كان إنتاجاً. كان يعالج قرارات توظيف حقيقية لحكومات حقيقية. ونعم، TCL 8.4 و8.6، في عام 2018.
أعرف ما تفكر فيه. دعني أوقفك.
TCL/TK ليست نكتة
رد الفعل الأول لمعظم المهندسين حين يرون TCL في stack إنتاجي هو “آه، لم يتحصلوا على وقت للهجرة بعد.” هذا الإطار خاطئ ويقود إلى قرارات سيئة.
TCL - Tool Command Language - وُلدت عام 1988 وصُمِّمت لشيء واحد بالضبط: البرمجة النصية وتجميع الأنظمة غير المتجانسة معاً. AOL Server، وهو runtime الويب في Talentera، يستخدم TCL كلغة امتداده الأصلية. AOL Server نفسه بنية تحتية جيدة فعلاً: مدفوع بالأحداث، متعدد الخيوط (مع thread pool، ليس خيط-لكل-طلب)، تجميع الاتصالات الفعّال مدمج. مشتق Naviserver الأصلي لا يزال قيد التطوير النشط لأن النموذج يعمل فعلاً. حين كان فريق البنية التحتية الويب في Google يعمل بمبادئ أولى في مطلع الألفية، استُشهد بمعمارية AOL Server بارتياح. هذا ليس إرثاً بالتحلل - إنه إرث بالطول، وهو شيء مختلف.
TCL 8.4 و8.6 ليسا أرقام إصدار ستختارهما في 2018 لو كنت تبدأ من الصفر، لكنهما يعنيان أن الـruntime ثابت. لا تغييرات كاسرة. لا إهمال مفاجئ. كل سطر كود TCL كُتب قبل وصولك لا يزال يعمل كما كُتب. لـSaaS متعدد المستأجرين حيث يؤثر وقت التوقف مباشرة على حكومات تُشغّل جولات توظيف لآلاف المرشحين، الاستقرار الممل ميزة تنافسية.
اللغة نفسها - Tcl لها جمال غريب. كل شيء string. الأوامر مجرد قوائم. نموذج التنفيذ قابل للتركيب بشكل لافت. لنوع معالجة الطلبات التي كانت Talentera تقوم به - تشكيل الاستعلامات، تجميع الاستجابات، معالجة البيانات المُهيكلة في حلقات ضيقة على جهة الخادم - إنها جيدة بصراحة. ليست رائعة، لكن جيدة. و”جيدة ومستقرة ومكتوبة أصلاً” تتفوق على “أفضل، لكن تتطلب إعادة كتابة” تقريباً في كل مرة حين لديك مستويات خدمة مؤسسية للحفاظ عليها.
التكلفة الحقيقية لـstack الإرث
إليك الرقم الصادق: التكلفة الحقيقية لـstack الإرث ليست الـruntime. إنها السطح التشغيلي.
AOL Server + TCL لا تمتلك قصة مراقبة حديثة غنية. إضافة distributed tracing حين نواتك TCL namespace تعمل داخل thread pool على نموذج خادم من عام 2003 يتطلب حل مشاكل حل لها النظام البيئي مفتوح المصدر بالفعل لـGo وJava وNode.js - لكن ليس لهذا. هذا وقت هندسة لا يذهب لبناء الميزات. حين انضممت، كان الفريق يُنفق قدراً غير متناسب من التحميل المعرفي على “كيف نحصل على رؤية لما تفعله TCL في الإنتاج” مقارنةً بـ”ماذا يجب أن تفعل TCL بعد ذلك.”
هذه هي المشكلة الحقيقية للحل. ليس “أعد كتابة TCL” - ذلك مشروع متعدد السنوات قد يُنهي الشركة - بل “قلل التحميل المعرفي لتشغيل TCL حتى يستطيع الفريق التركيز على بناء المنتج.”
الإجابة التي توصلنا إليها: لا تُهاجر نواة TCL. بدلاً من ذلك، عزلها.
DDD + microservices كغلاف لا بديل
النهج المعماري الذي قدته كان التصميم المدفوع بالدومين مُطبَّقاً حول نواة TCL، لا بدلاً منها. أصبحت Camunda BPMN منسّق سير العمل للعمليات التي كانت تصبح معقدة جداً للإدارة في TCL الخالص - سلاسل موافقة متعددة المراحل، تفريع شرطي لتهيئات عملاء حكوميين مختلفين، pipelines معالجة المستندات. Camunda تتحدث Java، مما يعني خدمات Java 8 تملك طبقة تعريف سير العمل، مع TCL تعمل كأحد أنواع العمال المنفّذين لتنفيذات مهام محددة.
دخل Keycloak لإدارة الهوية والوصول - OAuth2 وOpenID Connect عبر المشهد متعدد المستأجرين. كان هذا القرار الصحيح في 2018. قبل Keycloak، كانت هوية المستأجر تُدار بطريقة… يدوية. رموز جلسة مخصصة، منطق مصادقة لكل مستأجر مبعثر عبر معالجات الطلبات، دعم federation محدود. Keycloak مركزها، أعطانا مصادقة متوافقة مع المعايير، وسمح للخدمات الأحدث (Kotlin microservices وطبقات Node.js API) بالمشاركة في نفس نموذج الهوية دون الحاجة لفهم TCL session internals.
RabbitMQ تولّى الرسائل غير المتزامنة. Redis تولّى التخزين المؤقت. الميزات المحمولة الجديدة - Ionic ثم لاحقاً Flutter - تحدّثت إلى بوابات API لـNode.js ترجمت بين توقعات عملاء الموبايل وواقع الـbackend القائم على TCL. لم يتطلب شيء من هذا لمس نواة TCL. نواة TCL استمرت في التشغيل. الخدمات الجديدة نمت حولها كمدينة تنمو حول نهر.
الأسبوع الذي شحنت فيه Kotlin وTCL في نفس الـsprint
ثمة نوع محدد من الصدمة المعرفية يأتي من التبديل السياقي بين TCL 8.4 وKotlin في نفس الأسبوع. في TCL، تفكر في معالجة الـstrings وإدارة مساحة الاسم والتأكد من صحة قوائم معاملات الـproc. في Kotlin، تفكر في coroutines وdata classes وسلامة null. النماذج مختلفة لدرجة أنها تكاد تكون متعامدة.
ما وجدته، مما فاجأني، هو أن هذا جيد فعلاً. الفصل الصارم للاهتمامات الذي فرضته المعمارية - TCL تملك طبقة معالجة الطلب، Kotlin تملك خدمات الدومين الجديدة، Camunda تملك تعريفات العمليات - جعل التبديل السياقي نظيفاً. حين كنت في TCL كنت أفكر في مشاكل TCL. حين كنت في Kotlin كنت أفكر في مشاكل Kotlin. السياقات لم تتسرب لأن الحدود كانت حقيقية.
هذه إحدى الحجج المُقلَّلة التقدير للمعمارية المحدودة صراحةً: إنها تجعل المتاجر متعددة التقنية قابلة للتشغيل. إذا كنت تبني monolith وتحاول إدخال لغة جديدة، ستواجه ارتباكاً في الحدود في كل مكان - أين تنتهي PHP وتبدأ Node.js؟ حين تكون قد طبّقت DDD بشكل صحيح، الحد هو حد النشر، لا مجرد مساحة اسم. هذا الوضوح يهم حين يبدّل فريقك بين لغة برمجة نصية عمرها 30 عاماً ولغة JVM حديثة في نفس الـsprint.
كيف تبدو الهجرة فعلاً
بنهاية وقتي في Talentera كان لديّ رؤية واضحة لمسار الهجرة، حتى لو لم نكن قد أكملناه. الجواب ليس “أعد كتابة TCL.” الجواب هو:
- حدّد الدومينات حيث تنفيذ TCL يسبب مشاكل فعلية. ليس “هذا قديم”، بل “هذا يحدّ منا بشكل نشط” - أماكن حيث نقص نظام النوع الأغنى أو قصة المراقبة المحدودة يكلّف وقت هندسة حقيقي.
- عرّف حد خدمة جديد لذلك الدومين. امتلك البيانات لذلك الدومين في الخدمة الجديدة.
- اجعل Camunda تتوسط الانتقال - محرك سير العمل يستطيع توجيه المهام للخدمة الجديدة حين تصبح الخدمة الجديدة متاحة، بينما تنفيذ TCL يتعامل مع نفس المهام بالتوازي حتى تطمئن.
- حين تستنزف تنفيذ TCL من حركة المرور لذلك الدومين، تقاعده.
هذا جدول هجرة يُقاس بأرباع السنة، لا سنوات. ولا يتطلب إعادة كتابة كل شيء دفعة واحدة. الفهم الحاسم هو أن “الهجرة” ليست “الاستبدال” - إنها “إعادة التخصيص المنهجية للملكية.” TCL لا يحتاج الاختفاء. يحتاج فقط التوقف عن امتلاك الدومينات التي ليست جيدة في امتلاكها.
الشيء الذي لا أحد يخبرك به عن وراثة stacks القديمة
حين تنضم إلى شركة تشغّل stack كهذا، تكون هناك إغراء لإترك بصمتك باقتراح إعادة كتابة كاملة. مقاومة هذا الإغراء هي أحد أهم القرارات المعمارية التي ستتخذها. إعادة كتابة كاملة لـSaaS متعدد المستأجرين في الإنتاج هي مشروع من المخاطرة بالشركة. تكاد لا تنتهي في الجدول الزمني. الشيء الجديد نادراً ما يُكرّر جميع الحالات الحدية التي تعامل معها الشيء القديم. وأثناء إعادة الكتابة، لا تبني منتجاً جديداً - تبني بديلاً للمنتج الموجود. منافسوك لا يقفون.
المهارة الأكثر قيمة هي تعلّم قراءة stack بتعاطف. TCL على AOL Server ليست دَيناً تقنياً لأنها قديمة. إنها دَين تقني في أماكن محددة حيث يخلق نموذجها التشغيلي احتكاكاً حقيقياً. افهم أين تلك الأماكن. أصلح تلك الأماكن تحديداً. اترك الباقي.
شحنت Kotlin microservices في تلك البيئة. شحنت تنسيق سير عمل BPMN. شحنت هوية OAuth2 مُدمَجة عبر المستأجرين. نواة TCL استمرت في التشغيل طوال ذلك.
هذا هو العمل.