في 2013 كنت أكتب Java full-stack في BluLogix، أُصرّف إلى JavaScript باستخدام Google Web Toolkit، أُركّب Spring Security للمصادقة، وأشحن منصة فوترة سحابية لمشغّلي الاتصالات. TypeScript لن يصل إلى 1.0 قبل عام آخر. React كان مشروعاً داخلياً في Facebook. Node.js كان ما يكتب الناس عنه مقالات، لا ما يشحنون عليه أنظمة الفوترة.
كنت، دون أن أعرف ذلك، أعمل بـfull-stack آمن النوع قبل عقد من أن يصبح هذا هو الافتراضي.
ما كان GWT فعلاً
Google Web Toolkit كانت فكرة جريئة: اكتب الـfrontend بـJava، شغّل مُصرِّف GWT، واحصل على JavaScript محسَّن في الطرف الآخر. ليس wrapper رفيع. ليس شيئاً مشابهاً لـJSX. Java - كلاسات وgenerics وواجهات ونظام النوع الكامل - مُصرَّفة عبر cross-compilation إلى JavaScript يعمل في المتصفح.
نجح هذا لأن Google احتاجته أن ينجح. Gmail بُني على GWT. Google Docs بُني على GWT. استثمرت Google هندسة جادة في المُصرِّف - إزالة الكود الميت، تقسيم الكود، التحسين القائم على التصاريح عبر أهداف المتصفح - وأنتج مخرجات فعّالة بشكل مفاجئ. الـJS الناتج كان مُصغَّراً ومُصرَّفاً لكل متصفح، لذا IE يحصل على artifact واحد وFirefox على آخر وChrome على آخر. لم يكن عليك الاهتمام بذلك. المُصرِّف تعامل معه.
عرض القيمة لفريق كفريقي كان فورياً: نفس نظام النوع الذي يحمي الـbackend كان يحمي الـfrontend. إذا أرجع الـAPI كائن BillingCycle، استقبل كود الـUI كائن BillingCycle، مع كل حقوله ذات نوع محدد، ويلتقط المُصرِّف كل عدم تطابق في وقت البناء. لا اختلافات في شكل JSON. لا “اعتقدت أن هذا الحقل String”. لا مفاجآت في runtime لأن أحداً أعاد تسمية خاصية في DTO الـbackend ونسي تحديث طلب الـfetch في الـfrontend.
في 2013، كان هذا غير اعتيادي فعلاً. الجميع كانوا يكتبون jQuery callbacks ويصلّون.
الثمن الذي دفعته مقابل الأنواع الثابتة
GWT لم تكن مجانية. المُصرِّف كان بطيئاً. ليس “ثوانٍ إضافية قليلة” بطيئاً - دورات تصريف على تطبيق GWT متوسط الحجم تستغرق من دقيقتين إلى خمس دقائق. وضع التطوير التزايدي (حيث تُشغّل التطبيق في متصفح مُستضاف وتُبادل Java bytecode بشكل ساخن) ساعد، لكنه كان هشاً: وضع التطوير والتصريف للإنتاج كانا يتباعدان أحياناً بطرق دقيقة، وكنت تشحن شيئاً يعمل بشكل مثالي في التطوير لكن ينكسر في تصريح لم يمارسه متصفح التطوير أبداً.
نموذج الـwidget كان تسلسل كلاسات Java: GwtWidgets تمتد من كلاسات قاعدة مجردة، واجهات معالج الأحداث، قوالب UiBinder XML تُعيَّن على كلاسات Java. كانت هذه ثقافة Java المؤسسية لعام 2013 مُطبَّقة على الـUI. شعرت طبيعية إذا جئت من Swing أو JSF. شعرت غريبة إذا جئت من أي شيء آخر.
CSS كانت معركة حقيقية. نظام CssResource في GWT أعطاك أسماء كلاسات CSS آمنة النوع - كود Java الخاص بك كان يشير إلى myStyle.myClassName() بدلاً من string خام، لذا إعادة تسمية كلاس CSS ستنتج خطأ مُصرِّف بدلاً من كسر صامت. هذا رائع نظرياً فعلاً. في الواقع، كان الـtooling حوله خشناً بما يكفي لأقضي ساعات ذات معنى في تصحيح سبب عدم تطبيق style، لأجد في النهاية أنني أشرت إلى النوع الخاطئ من CssResource.
Spring Boot وصل وشعر كهدية
بدأت استخدام Spring Framework حوالي 2012 - Spring MVC لنقاط نهاية REST، Spring Security للمصادقة، Spring WS لخدمات SOAP (نعم، SOAP، هذا كان Java المؤسسي للاتصالات). كانت حقبة Spring بتهيئة XML: ملفات applicationContext.xml مكوّنة من 500 سطر من تعريفات الـbean والإعلانات عن مساحات الأسماء والـproperty placeholders. كان لديك علاقة حميمة مع سياق تطبيقك. كنت تعرف كل bean باسمه.
Spring Boot 1.0 صدر في أبريل 2014. كنت في AFAQ آنذاك، وتبنّيناه مبكراً - مبكراً بما يكفي بحيث لم تكن هناك بعد 400 إجابة Stack Overflow لكل مشكلة تهيئة تواجهها.
التحوّل الذي أحدثه Boot لم يكن إضافة ميزات. كان إزالة الطقوس. Auto-configuration كانت تعني أن الـframework يتخمّن الإعدادات الافتراضية المعقولة من الـclasspath الخاص بك ويُركّب ما تحتاجه فعلاً. @SpringBootApplication حل محل متاهة XML. Embedded Tomcat كان يعني تشغيل java -jar بدلاً من إدارة نشر container. application.properties حل محل ثلث XML الخاص بك.
المرة الأولى التي شغّلت فيها تطبيق Spring Boot من الصفر وحصلت على نقطة نهاية REST تعمل في أقل من 10 دقائق - بدون حساب الوقت اللازم لقراءة الوثائق حول ما كنت أفعله - أدركت أن شيئاً ما تغيّر. ليس ثورياً تقنياً. مختلف فلسفياً.
بناء EHR لـAFAQ بـGWT + Spring Boot
في AFAQ (2014-2017) كنت أبني جناحاً من EHR/EMR - قائماً على الويب، متكامل بشكل كامل مع وحدات ERP، منشور فوق مكونات VA VistA. الـstack التقني: GWT في الـfrontend، Spring Boot + Java EE في الـbackend، مع مكونات VistA MUMPS خلف طبقة خدمة (لديّ تدوينة كاملة أخرى عن ذلك الجانب).
التركيبة المحددة من GWT وSpring Boot كانت جيدة فعلاً للرعاية الصحية. إليك لماذا: واجهات الرعاية الصحية معقدة وذات حالة وعالية المخاطر. عرض مخطط المريض يحتوي على لوحات بوابية متداخلة، وتغذيات نتائج مختبر في الوقت الفعلي، وقوائم أدوية تُحدَّث خلال الجلسة، وملاحظات اللقاء بنص غني، وشارات تنبيه يجب أن تُطلَق حين يتغير شيء ما. نموذج واجهة Java في GWT تعامل مع هذا أفضل من سباغيتي jQuery. أمان النوع أهمّ حين البيانات كانت كائنات AllergyRecord وMedicationOrder لا JSON اعتباطي.
الإعدادات الافتراضية الرأيية لـSpring Boot ساعدت أيضاً في AFAQ لأن الفريق لم يكن كبيراً وكنا بحاجة للتحرك بسرعة. Convention over configuration سمح لنا بكتابة منطق الأعمال بدلاً من التجادل حول دورات حياة الـbean.
نقطة الألم كانت في البناء. تصريف GWT كامل للـfrontend، بالإضافة إلى بناء Maven لـSpring Boot للـbackend، على أجهزة 2014، كان عملية مدتها 15 دقيقة من الـcheckout النظيف إلى artifact قابل للنشر. كان علينا الانضباط حول عدم كسر البناء، لأن حلقة الـfeedback من “دمجت شيئاً خاطئاً” إلى “أعرف أنه مكسور” كانت ربع ساعة على الأقل.
لماذا ذهب GWT وما حل محله
GWT بلغ ذروته حوالي 2012-2013 ثم تحسّنت JavaScript بسرعة. TypeScript وصل وأعطى عالم JavaScript النوع الثابت الذي كان GWT يبيعه دائماً. React أعطاه نموذج المكوّن. Webpack أعطاه تقسيم الكود. أدوات البناء تحسّنت بشكل كبير. والأهم: كل مهندس ويب كان يعرف JavaScript أصلاً. مجموعة الناس القادرين على كتابة كود GWT Java للـfrontend كانت تقاطع “مهندسو Java” و”أشخاص مستعدون لكتابة frontend” - مخطط فين مشهور بصغره.
قلّلت Google تدريجياً استثمارها في GWT. استلأت المجتمع منه. التطوير الجديد في GWT الآن محدود داخل المحدود: ثمة أنظمة فوترة طبية لا تزال تشغّله، ومتاجر Java المؤسسية التي لم تهاجر أبداً، وعدد قليل من مشاريع المصدر المفتوح التي تستخدمه.
Spring Boot، في المقابل، سار في الاتجاه الآخر. ابتلع عالم الـbackend Java. إذا كنت تكتب خدمة Java جديدة في 2024، فأنت على الأرجح تستخدم Spring Boot. أصبح الافتراضي بطريقة لا تفعلها أُطر قليلة.
المفارقة أن الجزء من الـstack الذي كان أقل حداثة (الـbackends Java موجودة منذ 1997) تبيّن أنه الأكثر ديمومة. الجزء الذي كان متقدماً على زمانه فعلاً - Java frontend مُصرَّف آمن النوع - خسر أمام النظام البيئي الذي كان ينافسه حين نضج ذلك النظام.
ما سأقوله لشخص يصطدم بحائط الحنين لـGWT
إذا عملت في GWT وتشعر بالحنين: ما تفتقده فعلاً هو شعور نظام نوع متسق عبر الـstack. هذا الشعور متاح الآن في TypeScript + tRPC، أو في backend Java كامل مع عميل TypeScript مُولَّد، أو في Kotlin + Compose Multiplatform إذا كنت جريئاً. الأدوات أفضل بشكل كبير.
لكنك لست مخطئاً في أن GWT حل مشكلة حقيقية أولاً. حلّها بكثير من Java ومُصرِّف بطيء جداً ونموذج widget يشبه Swing. في 2013 كان هذا التبادل الصحيح. في 2024 لديك خيارات أفضل - ومعظم تلك الخيارات الأفضل موجودة لأن GWT وُجد أولاً، وأثبت القيمة، ودفع الضريبة التجريبية حتى يستطيع الجميع بناء شيء أنظف.
ما زلت أفكر في قوالب UIBinder XML لـGWT أحياناً. ليس بحنين. لكن بنوع محدد من الاحترام المحفوظ للأشياء التي كانت صعبة فعلاً وشحنتها للإنتاج على الرغم من ذلك.
Spring Boot أفكر فيه كل يوم. إنه في الـstack الخاص بي الآن.