شاركنا هذا المقال مٌبرمجي موقع قِبلة وقد شرحوا فيه لماذا لم يقوموا ببناء Backend للخدمة وكيف تمكنوا من حل مشكلة تحديد القبلة بدون أي بنية تحتية.
المقدمة - اتجاه ثابت لملايين المستخدمين المتفرقين جغرافيًا
كل يوم، يحتاج نحو ملياري مسلم حول العالم لمعرفة اتجاه واحد ثابت على الأرض — الكعبة المشرفة في مكة المكرمة — خمس مرات يوميًا. من الناحية الهندسية، هذه مسألة تحديد موقع (geolocation) مقترنة بحساب جيوديسي: نعرف موقع المستخدم لحظيًا، ونعرف موقع الكعبة الثابت، والمطلوب هو زاوية الاتجاه بينهما فوق سطح كروي.
أغلب الحلول المتاحة تطبيقات تتطلب تثبيتًا وحسابًا وصلاحيات وصول للموقع الجغرافي بشكل دائم. أردنا اختبار فرضية مختلفة: هل يمكن حل المشكلة بالكامل من المتصفح، دون تطبيق، ودون أي بنية تحتية خلفية؟ الإجابة تحولت إلى قرار معماري كامل، وليست مجرد تفصيلة تقنية.
القرار المعماري: لماذا "بدون Backend" اختيار تصميمي
عند تصميم أي أداة تتعامل مع بيانات موقع المستخدم، السيناريو التقليدي هو: خادم خلفي يستقبل الإحداثيات، قاعدة بيانات تخزن تفضيلات المستخدم وسجل مواقعه، وربما طبقة مصادقة (authentication) لربط البيانات بحساب. هذا النمط منطقي حين تحتاج مزامنة بين أجهزة متعددة، أو تحليلات مركزية، أو ميزات اجتماعية.
لكن أداة مثل Qebla.com لا تحتاج أيًا من ذلك فعليًا. المستخدم يفتح الصفحة، والمطلوب إجابة فورية: "في أي اتجاه أصلي؟". لا حاجة لمزامنة بين أجهزة، ولا لتحليل سلوك طويل المدى لتقديم القيمة الأساسية. بناءً على هذا، اتخذنا قرارًا معماريًا واضحًا:
• الحساب الجيوديسي (bearing calculation) ينفَّذ بالكامل على جهاز المستخدم، لحظة الطلب.
•لا توجد قاعدة بيانات مركزية لتخزين مواقع أو هويات المستخدمين.
• التفضيلات (طريقة حساب المواقيت، آخر موقع معروف) تُخزَّن محليًا عبر localStorage بدل جدول مستخدمين في قاعدة بيانات.
•مواقيت الصلاة تُجلب مباشرة من واجهة برمجية عامة (Aladhan API) عند الحاجة، وتُخزَّن مؤقتًا (cache) على الجهاز، بدل تمريرها عبر خادم وسيط.
المفاضلة (trade-off) هنا واضحة: نتخلى عن إمكانية التحليل المركزي والمزامنة بين الأجهزة، مقابل خصوصية أعلى، وتكلفة تشغيل شبه معدومة (لا خادم نديره أو نوسّعه)، وقابلية توسع تلقائية — لأن كل مستخدم يحمل "حمل معالجته" الخاص على جهازه، فلا يوجد عنق زجاجة مركزي مهما زاد عدد المستخدمين.
الحساب الجيوديسي: مسار الدائرة العظمى، لا خط مستقيم على خريطة
خطأ شائع هو حساب الاتجاه برسم خط مستقيم بين نقطتين على إسقاط خريطة مسطح. على سطح كروي، هذا غير دقيق، وأحيانًا بفارق عشرات الدرجات حسب المسافة. الحل الصحيح معادلة الاتجاه الأولي لمسار الدائرة العظمى (great-circle bearing):
function greatCircleBearing(lat1, lon1, lat2, lon2) { const toRad = (deg) => (deg * Math.PI) / 180; const toDeg = (rad) => (rad * 180) / Math.PI; const φ1 = toRad(lat1); const φ2 = toRad(lat2); const Δλ = toRad(lon2 - lon1); const y = Math.sin(Δλ) * Math.cos(φ2); const x = Math.cos(φ1) * Math.sin(φ2) - Math.sin(φ1) * Math.cos(φ2) * Math.cos(Δλ); const θ = Math.atan2(y, x); return (toDeg(θ) + 360) % 360; // تطبيع 0-360} const MECCA = { lat: 21.4225, lon: 39.8262 };const qiblaBearing = greatCircleBearing(userLat, userLon, MECCA.lat, MECCA.lon);هذا الكود بالكامل — الذي يمثل "منطق الخادم" في أي تصميم تقليدي — ينفَّذ في المتصفح، باستخدام إحداثيات من Geolocation API. لا تُرسَل هذه الإحداثيات إلى أي خادم؛ الحساب يحدث ويُعرَض النتيجة فورًا.
من زاوية رقمية إلى بوصلة حية: التعامل مع مستشعرات الجهاز
زاوية الاتجاه بالدرجات لا قيمة عملية لها بدون ربطها باتجاه الجهاز الفعلي. هنا يدخل Device Orientation API، عبر حدث deviceorientationabsolute (مع معالجة خاصة لخاصية webkitCompassHeading في متصفح Safari على iOS، التي تُبلّغ عن الشمال الحقيقي بطريقة مختلفة):
window.addEventListener("deviceorientationabsolute", (event) => { const compassHeading = event.alpha; // اتجاه الجهاز نسبة للشمال الحقيقي const rotation = qiblaBearing - compassHeading; arrowElement.style.transform = `rotate(${rotation}deg)`;});تفصيلان دقيقان يحددان دقة هذا التطبيق عمليًا:
• الشمال المغناطيسي مقابل الشمال الحقيقي: مستشعرات البوصلة الخام تُبلّغ عن الشمال المغناطيسي، الذي يختلف عن الشمال الحقيقي بمقدار الانحراف المغناطيسي المحلي (magnetic declination)، وهي قيمة تتغير جغرافيًا وزمنيًا. تجاهل هذا التصحيح هو السبب الأكثر شيوعًا لعدم دقة تطبيقات القبلة.
• تشويش المستشعر: بيانات الاتجاه الخام تهتز، ومرشّح بسيط للمتوسط المتحرك (moving average) يجعل حركة السهم سلسة.
إدارة الحالة بدون قاعدة بيانات
بدل جدول مستخدمين تقليدي، تُحفظ التفضيلات (طريقة حساب المواقيت مثل أم القرى أو الهيئة المصرية العامة للمساحة أو ISNA، وآخر موقع معروف) في localStorage على جهاز المستخدم نفسه. النتيجة: الموقع "يتذكر" المستخدم دون تسجيل دخول، ودون أي جدول بيانات نديره أو نؤمّنه أو نعرّضه لاختراق محتمل.
لضمان عمل الأداة حتى بلا اتصال إنترنت، نستخدم عامل خدمة (service worker) بسيطًا يعرض آخر نسخة مخزَّنة من الصفحة والبيانات عند انقطاع الشبكة، مع إشارة واضحة أن العرض قد يكون غير محدَّث. هذا نمط "offline-first" شائع في تطبيقات الويب التقدمية (PWA)، ويعمل هنا دون أي طبقة مزامنة خلفية معقدة.
متى يصبح الـ Backend ضروريًا فعلًا؟
القرار بعدم بناء خادم خلفي ليس مبدأً مطلقًا؛ إنه نتيجة لتحليل المتطلبات الفعلية. لو احتجنا مستقبلًا ميزات مثل حسابات مستخدمين لمزامنة تفضيلات عبر أجهزة متعددة، أو تنبيهات صلاة مجدولة تُرسَل من الخادم حتى لو كان المتصفح مغلقًا، أو تحليلات استخدام مجمّعة لتحسين المنتج — عندها يصبح الـ Backend ضرورة هندسية حقيقية، لا خيارًا اختياريًا.
الدرس الأعمق هنا لهندسة الأنظمة: القرار الصحيح بشأن البنية التحتية ليس "ابنِ كل شيء احتياطًا للمستقبل"، بل "ابنِ ما يحتاجه المنتج الآن، وأضف الطبقة التالية حين يصبح لها مبرر فعلي مدعوم بمتطلبات حقيقية". كل طبقة بنية تحتية لا تُبنى هي طبقة لا تحتاج تأمينًا ولا صيانة ولا تفسيرًا في سياسة خصوصية.
الخلاصة
بُنيت Qebla.com بالكامل على أربعة عناصر أساسية في المتصفح:
- الـ Geolocation API
- وDevice Orientation API،
- وlocalStorage،
- وservice worker
— عناصر موجودة منذ ما يقارب عقدًا من الزمن. كانت كافية لحل مشكلة يحلها كثير من المنافسين بتطبيق أصلي كبير الحجم وشاشة تسجيل إلزامية وخادم خلفي دائم التشغيل.
الأداة متاحة مجانًا وبالكامل على qebla.com، بدون تثبيت وبدون تسجيل، وتعمل على أي هاتف يحتوي مستشعر بوصلة.
Discussion