بعض التفاصيل محجوبة. إن كان لديك مفتاح وصول، فبإمكانك مطالعة النسخة الكاملة.
متجر متخصّص في الشاحنات الصغيرة التجارية
تجديد موقع متجر متخصّص في الشاحنات الصغيرة التجارية، وإضافة محاكي تسعير
أعدتُ بناء موقع Wix القديم ليصير موقعًا للزوّار على SolidStart ونظامَ إدارة محتوى على Nuxt، وأضفتُ محاكي تسعير يتعامل مع 114 نقطة سعر. وقد نزل زمن العرض الأوّل من أكثر من 3 ثوانٍ إلى أقلّ من ثانية.

نظرة عامة
أعدتُ بناء موقع وكيلٍ يعمل في تجهيز مركبات تويوتا هايس ونيسان كارافان. كان الموقع القديم على Wix، يستغرق عرضه الأوّل أكثر من 3 ثوانٍ، ولم تكن فيه أيّ وسيلة لإخراج عرض سعر عبر الإنترنت. أمّا الموقع الجديد فقد تولّيتُه وحدي من التصميم إلى النشر، وحصرتُ العرض الأوّل في أقلّ من ثانية.
فصلتُ اختيار التقنية بين جانب الزائر وجانب التشغيل: جانب الزائر تصيير من الخادم بـ SolidStart، وجانب التشغيل نظام إدارة محتوى بـ Nuxt. وكلاهما يعمل على Cloudflare Workers ويتشاركان قاعدة D1 نفسها. والمشروع كلّه مستودع أحادي على Turborepo، ومنطق التسعير مفصول في حزمة مستقلّة يستعملها الجانبان معًا.
الميزة الرئيسة: محاكي تسعير تجهيز المركبات الجديدة
يتعامل المحاكي مع 13 مواصفة هيكل من المصنّع و23 خطّة تجهيز، وينتج تقاطعُهما 114 نقطة سعر قابلة للتسعير. ولكلّ نقطة سعر مجموعةُ الخيارات التي يمكن انتقاؤها عندها. والخيارات 49 نوعًا منظّمة في 8 مجموعات، ومعنى المجموعة أحادية الاختيار غير معنى المجموعة متعدّدة الاختيار. أمّا التوليفات فتبلغ في مجموعها نحو 8.6×10¹⁰ احتمالًا.
الإعدادات كلّها في D1، ويحرّرها فريق التشغيل من نماذج نظام إدارة المحتوى. فله أن يغيّر الطرازات والأسعار والخيارات ونطاق انطباقها كما يشاء، دون أن يرى JSON ودون أن يكتب صيغة قاعدة. وإن لزم نوعٌ جديد من العلاقات، أضفتُه أنا إلى المحرّك وهيّأتُ له نموذجًا مكتوبًا بلغة العمل.
البنية
تتشارك ثلاثة Workers قاعدة D1 نفسها. موقع الزوّار للقراءة في الأساس، وليس فيه من مسارات الكتابة سوى اثنين: إرسال الاستفسار، وعدّاد «يهمّني». ونظام إدارة المحتوى يتولّى القراءة والكتابة كلّها، ويعمل Worker الـ cron الذي يجلب من YouTube وInstagram كلّ ستّ ساعات.
لا يلمس واجهات البرمجة الخارجية إلا Worker الـ cron، وبيانات اعتماد الطرف الثالث ليست موضوعةً إلا فيه، فلا يد لموقع الزوّار ولا لنظام إدارة المحتوى عليها. وتُكتب نتائج الجلب لقطةً في D1، فإن أخفق الجلب سُجّل الخطأ فحسب دون أن يدهس البيانات القائمة، وتبقى في الصفحة الرئيسة محتوياتُ آخر جلبٍ ناجح كما هي.
المواضع الصعبة تقنيًّا
الإبطال المتسلسل، والتكرار حتى يسكن التغيير
إلغاء خيار واحد قد يُبطل خيارًا كان يعتمد عليه، وقد يجرّ ذلك الإبطالُ طبقةً تالية بدوره. لذلك يمسح محرّك القواعد مجموعةَ المنتقى في حلقة while-changed، ويكرّر حتى تمرّ دورة كاملة دون أن يتغيّر شيء. فالمسح مرّةً واحدة يترك اختيارات معلّقة في الهواء.
المحرّك حزمة دوالّ خالصة بلا أيّ اعتماد وقت التشغيل: لا يعرف DOM ولا إطار عمل ولا قاعدة بيانات. ولهذا يستطيع جانبُ الزائر ومعاينةُ نظام إدارة المحتوى أن يتشاركا الحكم نفسه، ويمكن عرضه على اختبارات الوحدة كما هو. وكلّ منطق يمسّ المبالغ مفصولٌ على هذه الصورة.
تحويل البيانات المنقولة إلى قاموس
لو حُملت البيانات متداخلةً على وجهها، لتكرّرت قائمة الخيارات كاملةً مع كلّ نقطة سعر. فمع 114 نقطة سعر تبلغ علاقات الانطباق 2,837 علاقة، وكان التعريف الواحد يُسلسَل مرارًا. لذلك صار الخادم يرسل قاموسًا في المستوى الأعلى ومراجعَ بالمعرّفات فقط، ويعيد العميل تركيبها مرّةً واحدة. ولم أمسّ المحرّك ولا الواجهة.
قطع الدوائر في الاعتماديات عند الحفظ
لو ضبط فريق التشغيل الإعداد على أن «A يحتاج B، وB يحتاج A»، لتعذّر انتقاء الاثنين إلى الأبد في جانب الزائر. لذلك يُجري نظام إدارة المحتوى قبل الكتابة بحثًا بالعمق أوّلًا بترقيم ثلاثي الألوان، فإن وجد دائرة رفض الحفظ. وحينها يُترجم مسارَ المعرّفات إلى أسماء الخيارات قبل عرضه.
ويوقف الفحصُ نفسه نوعين آخرين من الإعدادات المسدودة: أن يكون الطرف المعتمَد عليه داخل مجموعة أحادية الاختيار نفسها، وأن يكون غير مشمول بخيارات تلك النقطة السعرية. وكلاهما يُنتج في جانب الزائر خيارًا ظاهرًا لا يمكن ضغطه أبدًا.
قرارات أخرى
- لا تُحفظ نتيجة التسعير في قاعدة البيانات. فرابط المشاركة لا يحمل سوى معرّف نقطة السعر ومعرّفات الخيارات، ويعيد الخادم الحساب من D1 ويمرّره على محرّك القواعد مرّةً أخرى. فلا سبيل إلى تحرير الرابط لتوليد توليفة غير سليمة ورفع المبلغ
- الخيارات التي لا تتحقّق شروطها تبقى ظاهرة لكن معطّلة، ومعها سببها: «يتطلّب كذا» أو «لا يُجمع مع كذا». وذلك ليرى العميل بعينه فروقَ كلّ طراز
- يُولَّد ملفّ PDF لعرض السعر في المتصفّح. وتُحمَّل jsPDF تحميلًا ديناميكيًّا عند الضغط، فلا تدخل في ناتج التصيير من الخادم ولا في الحزمة الرئيسة
- إعدادات التسعير تُقرأ أضعافَ ما تُكتب، فتُوضع نتيجة الخادم في ذاكرة الحافّة بمدّة صلاحية خمس دقائق
- الاختبارات التي تمسّ D1 تُشغَّل على workerd الحقيقي، وتُبنى قاعدة البيانات من ملفّات الترحيل الأصلية. فقاعدة البيانات لا تُحاكى

