ثلاثة اعتبارات حاسمة قبل نشر أول تطبيق متكامل (Full Stack App) لك
يُعد بناء تطبيق متكامل (Full Stack App) مسعىً ليس بالهين، ويأتي نشر هذا التطبيق إلى بيئة الإنتاج (production environment) مع مجموعة خاصة به من الاعتبارات التي يجب أخذها في الحسبان. بصفتي مطور ألعاب لوحية، قمت مؤخرًا بنشر متتبع بسيط لألعاب تقمص الأدوار (roleplaying game tracker) يستخدم حزمة M-E-V-N stack. خلال عملية نشر التطبيق، توصلت إلى ثلاث نقاط رئيسية قد تكون مفيدة لك بينما تبدأ في التفكير في أفضل طريقة لنقل مشاريعك من مرحلة التطوير إلى مرحلة الإنتاج.
1. استراتيجية نشر الواجهة الأمامية والخلفية: معًا أم منفصلين؟
يمكن نشر الواجهة الأمامية (front end) والواجهة الخلفية (back end) لتطبيقك معًا أو بشكل منفصل، اعتمادًا على مدى تعقيد التطبيق. إحدى العقبات التي تظهر فورًا عند التفكير في الإنتاج هي السؤال الهيكلي حول كيفية نشر الواجهتين الأمامية والخلفية لتطبيقك. هل يجب أن تعيش الواجهة الأمامية (client) أو الملفات الثابتة (static files) في نفس مكان الخادم (server) وقاعدة البيانات (database)؟ أم يجب أن تكون منفصلة، مع قيام الواجهة الأمامية بإجراء طلبات HTTP من مكان آخر إلى الواجهة الخلفية باستخدام CORS؟
الإجابة ليست واحدة للجميع، حيث سيعتمد ذلك على بنية تطبيقك ومدى تعقيده. في متتبع ألعاب تقمص الأدوار الذي ذكرته سابقًا، قمت بتشغيل الحزمة بأكملها على Heroku dyno واحد، مع الهيكل التالي للمجلدات:
تعيش جميع ملفات الواجهة الأمامية والخلفية في نفس المكان، مع بناء الواجهة الأمامية (Vue client) للإنتاج في مجلد يقع في المسار /client/dist. في ملف server.js، بالإضافة إلى مجموعة من شيفرات قاعدة البيانات والتوجيه (routing code)، يوجد سطر صغير ينص على:
server.use(serveStatic(__dirname + "/client/dist" ));
في إطار عمل Express، يخبر هذا السطر التطبيق بتقديم ملفات الواجهة الأمامية الثابتة من مجلد معين، ويمكّنني من تشغيل الواجهتين الأمامية والخلفية ضمن نفس البيئة. إذا كنت تنشر تطبيقًا بسيطًا بالمثل، فقد يعمل هذا النوع من الحلول معك أيضًا.
على العكس من ذلك، واعتمادًا على تعقيد مشروعك، قد تضطر إلى فصل الواجهتين الأمامية والخلفية والتعامل معهما كتطبيقين منفصلين، وهذا ليس بالأمر الجلل. في التطبيق المذكور أعلاه، تقوم الواجهة الأمامية (client) بإجراء استدعاءات لنقاط نهاية API ثابتة (static API endpoints) يتم التعامل معها بواسطة الخادم، مثل هذا المثال:
getQuests: function ( ) {
axios
.get( 'https://mevn-rpg-app.herokuapp.com/quests' )
.then( response => ( this .questData = response.data))
}
من الناحية الفنية، يمكن للواجهة الأمامية الخاصة بي أن تجري هذه الطلبات من أي مكان – حتى من موقع GitHub Pages ثابت. يمكن أن يساعد هذا النوع من الحلول في فصل تطبيقك إلى كيانين متميزين للتعامل معهما، وهو ما يكون أفضل أحيانًا من محاولة حشر المشروع بأكمله في موقع واحد. ملاحظة مهمة: إذا كنت ستقوم بإجراء طلبات HTTP عبر النطاقات (cross-origin HTTP requests) – أي طلبات من واجهة أمامية تعيش على نطاق (domain) منفصل عن API أو الخادم – فستحتاج إلى التعرف على CORS (مشاركة الموارد عبر النطاقات). فهم CORS ضروري لضمان التواصل الآمن والفعال بين الموارد المختلفة.
2. تهيئة الشيفرة لبيئة الإنتاج: وداعًا للمحلي!
عندما تكون غارقًا في عملية التطوير، قد يكون من السهل أن تفقد تتبع مدى اعتماد شيفرتك على الملفات المحلية أو البيانات الأخرى. فكر في المثال التالي في ملف server.js يعمل محليًا:
server.listen( 3000 , () => console .log( "Server started!" ));
على جهاز محلي، تطلب الشيفرة من الخادم الاستماع على المنفذ 3000 وتسجيل رسالة في وحدة التحكم (console) تفيد بأننا جاهزون للانطلاق. ولكن في بيئة الإنتاج، ليس لدى الخادم أي مفهوم عن مكان "localhost" أو على أي منفذ 3000 يجب أن يستمع. في هذا المثال، سيتعين عليك تغيير شيفرتك إلى شيء مثل:
const port = process.env.PORT || 3000 ;
server.listen(port, () => console .log( "Server started!" ));
يوجه ما سبق الخادم للاستماع بدلاً من ذلك على المنفذ 3000 للعملية التي تعمل حاليًا، أينما كانت (process.env.PORT هي متغير بيئة يتم توفيره بواسطة منصة الاستضافة لتحديد المنفذ الذي يجب أن يستمع عليه التطبيق). هذا يضمن أن تطبيقك يمكنه التكيف مع المنفذ المخصص له في بيئة الإنتاج.
وبالمثل، في تطبيقي، لدي العديد من الوحدات (modules) التي تحتاج إلى استيراد بعضها البعض لتعمل:
في ملف /routes/Quests.js، على سبيل المثال، لدي موجه (router) يخبر الخادم بما يجب فعله عند تلقي طلبات API للتفاعل مع العناصر المتعلقة بالمهام في قاعدة البيانات. يحتاج الموجه إلى استيراد مخطط Mongoose schema من /models/quest.js ليعمل بشكل صحيح. إذا كان التطبيق يعمل محليًا، يمكننا ببساطة أن نقول:
const Quest = require ( '../models/quest' );
بسيط جدًا! ومع ذلك، للأسف، لن يعرف خادمنا مكان العثور على الدليل الجذري (root directory) لمشروعنا بمجرد نشره. في Express، سنغير شيفرتنا إلى شيء مثل:
const path = require ( 'path' );
const Quest = require (path.join(__dirname, '../models/quest' ));
قد تختلف حالتك الخاصة، اعتمادًا على لغتك وإطار العمل (framework) الذي تستخدمه، ولكن ستحتاج إلى أن تكون محددًا بشأن شكل شيفرتك في بيئة الإنتاج بدلاً من بيئة التطوير المحلية. بالإضافة إلى ذلك، ربما تكون على دراية بأي أداة تجميع (bundler) تستخدمها للواجهة الأمامية (مثل webpack)، وستحتاج إلى بناء الواجهة الأمامية (client) للإنتاج لتحسينها للنشر.
3. اختيار منصة النشر المناسبة: بحر من الخيارات!
إذا كنت قد قمت بنشر موقع ويب للواجهة الأمامية (front end website) أو أي نوع آخر من التطبيقات الثابتة (static app)، فقد تكون معتادًا على مجرد دفع ملفاتك إلى مستودع بعيد (remote repository) والانتهاء من الأمر. إن نشر تطبيق متكامل (full stack app) (أو حتى مجرد واجهة خلفية back end) أكثر تعقيدًا بكثير. ستحتاج إلى خادم مخصص (dedicated server)، أو شيء يحاكيه، للاستجابة لطلبات HTTP التي سيتلقاها والعمل مع قاعدة بيانات عبر الإنترنت (online database).
هناك عدد من الخدمات التي ستقوم بهذا الأمر نيابة عنك، ويتراوح الطيف بناءً على السعر، قابلية التوسع (scalability)، التعقيد، وعوامل أخرى. هناك العديد من المقالات التي تقارن خيارات PaaS (البرمجيات كخدمة) للنشر، ولكن إليك بعض الأفكار بينما تفكر في المنصات لمشروعك الأول:
Heroku: إذا كان لديك مشروع صغير أو كنت ترغب فقط في التعرف على النشر، فقد تكونHerokuخطوة أولى جيدة. توفر بيئة سهلة الاستخدام للمبتدئين.AWS،Docker، وKubernetes: إذا كنت تسعى للحصول على وظيفة في تطوير الويب المتكامل (full stack web development) أوDevOps، فالآن هو الوقت المناسب للتعرف على خدمات الويب من أمازون (Amazon Web Services) و/أو منصات الحاويات (container platforms) مثلDockerوKubernetes. هذه التقنيات أساسية في البيئات الكبيرة والمعقدة.Azure: إذا كنت مطورC#أو.NET، يبدوAzureطريقة سلسة لنشر تطبيقاتك دون الحاجة إلى مغادرة أمان نظامMicrosoftالبيئي.
هناك، بالطبع، العديد من الخيارات الأخرى المتاحة، وقد يعتمد سيناريو استخدامك الخاص على التسعير أو مجموعات الميزات المحددة المعروضة. بالإضافة إلى ذلك، ستحتاج إلى التفكير في أي إضافات (addons) ستكون ضرورية لتكرار وظائف تطبيقك في بيئة الإنتاج. على سبيل المثال، يستخدم متتبع ألعاب تقمص الأدوار الخاص بي MongoDB، ولكن نسخة الإنتاج بالتأكيد لا يمكنها استخدام قاعدة البيانات الصغيرة على جهازي المحلي! بدلاً من ذلك، استخدمت إضافة mLab Heroku addon لتشغيل الموقع المباشر بنفس الوظائف كما في بيئة التطوير الخاصة بي.
يعتمد نجاح تطبيقك، وكذلك تقدمك كمطور ويب متكامل، على قدرتك على النظر في خيارات النشر وإنشاء مسار ناجح للإنتاج. بقليل من البحث، أنا متأكد من أنه يمكنك العثور على أفضل حل يناسب جميع احتياجات تطبيقك.
الخلاصة التقنية
إن نشر أول تطبيق متكامل هو نقطة تحول حاسمة في رحلة أي مطور. يتطلب الأمر فهمًا عميقًا للفروق بين بيئات التطوير والإنتاج، وتخطيطًا دقيقًا لاستراتيجية فصل أو دمج الواجهتين الأمامية والخلفية، واختيارًا مدروسًا لمنصة الاستضافة. من خلال تبني أفضل الممارسات في تهيئة الشيفرة، والاستفادة من متغيرات البيئة، واستكشاف حلول النشر السحابية، يمكن للمطورين ضمان انتقال سلس وفعال لتطبيقاتهم إلى بيئة الإنتاج، مما يضع الأساس لنجاح طويل الأمد وقابلية للتوسع.