الإقليم ٩ — التدفّق الكامل: بايتةٌ من النقرة إلى الصفّ ورجوعاً
النبذة
هذه هي الصورةُ التي طلبتَها من أول يوم: "أعرف كيف تتدفّق البيانات بوضوح." بنينا كلَّ صندوقٍ على حدة — HTTP، Node، Express، SQL، Prisma، Zod، الهويّة، React. الآن نوصّلها ونتتبّع بايتةً واحدةً في رحلتها الكاملة: من نقرةِ المتدرّب حتى صفٍّ في Postgres ورجوعاً إلى شاشةٍ تتحدّث. لا شيءَ جديدٌ هنا — وهذا هو المقصود. كلُّ نقطةٍ بدت متفرّقةً تتجمّع الآن في "آها" واحدة. وفي النهاية تملك أداةً عمليّةً أقوى من أي لبنة: تشخيصُ أي عطلٍ بمعرفةِ أي طبقةٍ هو فيها.
اللغز المستفزّ
قبل أن نتتبّع، توقّع. لطلبِ تسجيلٍ واحد (POST /api/v1/auth/register)، اكتب بقلمك:
- كم حدّاً (boundary) تعبره البياناتُ من النقرة إلى الصفّ؟ وعند كلٍّ، ما شكلُها (كائنُ JS؟ نصُّ JSON؟ بايتاتُ TCP؟ صفٌّ؟) وهل هي مكتوبةُ النوع أم لا؟
- أين بالضبط يقف كلٌّ من الحرّاس الثلاثة (مترجم TS / Zod / قيودُ القاعدة) في المسار؟
- لو خزّن الخادمُ كلمةَ المرور، أين تماماً تُجزّأ، وعلى أي خيط؟
- الردُّ يعود
201بمستخدمٍ بلا حقلpassword— أين حُذف، ولماذا؟
إن ترددتَ في أيٍّ، فالصورةُ لم تكتمل بعد. تتبّعها معي الآن، وستكتمل.
المسارُ الكامل (الكتابة): POST /auth/register
لنتتبّع كلمةَ المرور "hunter2" في رحلتها. كلُّ خطوةٍ موسومةٌ بإقليمها.
ثماني عشرة خطوة، تسعةُ أقاليم، حدودٌ كثيرة — وكلٌّ بنيتَه بيدك. هذا "تدفّقُ البيانات بوضوح".
رحلةُ النوع (خيطُ الـ erasure، مرئياً)
تتبّع النوعَ وحده عبر المسار، فترى لماذا احتجنا كلَّ حارس:
النوعُ يُولَد ويُمحى مرّاتٍ عبر الحدود. الحرّاسُ الثلاثة هم نقاطُ إعادةِ التوليد: المترجمُ يحرس المصدر (ثم يُمحى)؛ Zod يعيده عند حدّ الشبكة (٦)؛ Prisma يعيده عند حدّ القاعدة (٥)؛ وقيودُ Postgres تحرس الصحّةَ الحتميّةَ في القاع (٤). لا طبقةَ تكفي وحدها — كلٌّ تحرس زمناً ومجالاً مختلفاً. الآن ترى لماذا.
المسارُ الكامل (القراءة): GET /me — انعدامُ الحالة حياً
تأمّل الجوهر: الخادمُ عرفَ من المُرسِل دون أن يحفظ عنه شيئاً — الإثباتُ محمولٌ وموقَّع (٧)، فبقي عديمَ الحالة (٠)، فيتوسّع أفقياً (بنية الويب). الصورةُ المعماريّةُ الكبرى التي رسمها منهجُ البنية كصناديقَ سوداء، امتلأت الآن بالآليّات:
أين تعيش الحالةُ وأين لا تعيش: الخادمُ (Node/Express) عديمُ الحالة — أيُّ نسخةٍ تخدم أيَّ طلب. الحالةُ كلُّها في Postgres (٤). والهويّةُ يحملها العميلُ في الـ JWT (٧). هذا الثالوثُ هو لماذا يتوسّع النظامُ أفقياً بلا التصاقٍ بنسخة — وعدُ منهجِ البنية، محقَّقاً بآليّاتٍ بنيتَها.
اللغز / التمرين: شخّص بالنموذج الذهنيّ
لا بناءَ جديدٌ هنا — تمرينٌ يحوّل فهمَك إلى مهارةِ إدارةٍ عمليّة: تشخيصُ العطل بمعرفةِ طبقته. لكل عَرَضٍ، حدّد الطبقةَ بالضبط والسبب الجذريّ، مستنداً لرحلة التدفّق — قبل أن تشغّل أي شيء. ثم افتعِل كلَّ عطلٍ وتحقّق:
أ — تشخيصٌ نظريّ (اكتب الطبقة + الجذر لكلٍّ):
- الواجهةُ تُظهر "CORS error" والطلبُ لم يصل المتحكّم. أي طبقة؟ أيُّ middleware غائبٌ أو خاطئ؟ (٣)
- كلُّ طلبٍ يردّ
401رغم أنك سجّلتَ الدخول. أين الخلل المحتمل؟ (٧ — الرمز لا يُرسَل؟ السرُّ مختلفٌ بين الإصدار والتحقّق؟ منتهٍ؟) - التسجيلُ يردّ
400معdetails.password. أيُّ حارسٍ نطق، ولماذا ليس المترجم؟ (٦ + خيطُ الـ erasure) - التسجيلُ المكرّرُ السريعُ ينشئ مستخدماً واحداً فقط ويرفض الثاني بخطأٍ من القاعدة. أيُّ حارسٍ أنقذك، ولماذا لم يكفِ فحصُ التطبيق؟ (٤ + idempotency من ٠)
- طلبٌ واحدٌ بطيءٌ يجمّد كلَّ الطلبات الأخرى. أي طبقة؟ ما القاعدةُ المنتهَكة؟ (٢ — عملٌ محسوبٌ على خيط الحلقة)
- الردُّ يصل لكنه يتجمّد ناقصاً في المتصفّح. أي طبقة؟ (٠ — Content-Length كاذب / تأطيرٌ مكسور)
- صفحةُ المناهج تعرض قائمةً قديمةً بعد إضافةِ منهج. أي طبقة؟ (٨ — الحالةُ لم تتحدّث / تبعيّةُ useEffect)
ب — افتعِل وتحقّق. لكلِّ عَرَضٍ أعلاه، اصنعه عمداً في مشروعِ تجربةٍ (احذف cors، غيّر JWT_SECRET بين الإصدار والتحقّق، أرسل كلمةً قصيرة، اكتب Content-Length خاطئاً يدوياً عبر nc، ضع حلقةَ حسابٍ في معالِج...)، وراقِب العَرَضَ يظهر كما تنبّأت. التطابقُ بين تنبّؤك والواقع هو الدليلُ أن النموذجَ في رأسك صحيح — وهذه هي القدرةُ التي طلبتَها: تدير المشروعَ لأنك تعرف أين كلُّ شيء ولماذا.
ج — ارسمها لنفسك. أعِد رسمَ مخطّطِ التدفّق الكامل (الكتابة والقراءة) من ذاكرتك على ورقةٍ بيضاء، واسِماً كلَّ حدٍّ وكلَّ حارسٍ وكلَّ تحوّلِ شكلٍ للبيانات. إن استطعتَ رسمَه بلا النظر، فالصورةُ اكتملت فيك.
هذا الإقليمُ بلا "حلٍّ مخفيّ" — هو خريطتُك أنت. أتقِنه يعني أنك تستطيع وضعَ إصبعك على أي عطلٍ في الستاك دون تخمين. هذا جوهرُ "إدارةِ المشروع".
الخلاصة — الشجرةُ تكتمل
- الالتحام: الشجرتان اللتان نبتتا متوازيتين — الستاكُ من جهة، ومعرفتُك السابقة (الشبكات/Docker/C/أنواع TS) من جهة — التحمتا في مسارٍ واحدٍ متّصل. كلُّ إقليمٍ صار حلقةً مرئيّةً في رحلةِ بايتة.
- العقدة الجديدة (وهي الكلّ): تدفّقُ البيانات كنظامٍ واحدٍ متّصل — لا صناديقَ منفصلة، بل خطٌّ تتبعه بإصبعك من النقرة إلى الصفّ ورجوعاً، وتعرف عند كل نقطةٍ: الشكلَ، النوعَ، الحارسَ، والخيط.
- الباقي: كلُّ endpoints مشروعك تتبع هذا المسار إلا واحداً. واحدٌ لا يكتفي بقراءة/كتابة القاعدة — بل يشغّل كودَ C غريباً أرسله المتدرّب، بأمان، ويصحّحه. الصندوقُ الأخير.
الوصلة للواقع (نمط SelfLab)
افتح مشروعك الآن واتبع POST /auth/register بإصبعك عبر الملفّات الفعليّة، وستمرّ بكل خطوات المخطّط: frontend (fetch) → الشبكة → app.ts (السلسلة) → user.routes.ts (التوجيه) → user.schema.ts + safeParse (Zod) → user.controller.ts (المتحكّم) → bcrypt.hash → prisma.user.create → schema.prisma/القيود → Postgres → ورجوعاً بمظروفٍ بلا password. لم يبقَ سطرٌ واحدٌ في مسار التسجيل غامضاً عليك. وما ينقص مشروعَك (login، /me، البادئة /api/v1، helmet/cors/rate-limit، معالِجُ الأخطاء، الخدمة، بقيّةُ الكيانات، الواجهة) — تعرف أين يقع كلٌّ في هذا المسار، وكيف تبنيه.
البذرة الأخيرة: كلُّ ما تتبّعناه كان بياناتٍ تُكتَب وتُقرَأ. لكن POST /tasks/:taskId/check مختلف: يأخذ كودَ C من متدرّبٍ — كودٌ غريبٌ قد يكون خبيثاً أو لا نهائياً — ويجب أن يصرّفه ويشغّله ضد حالاتِ اختبارٍ ويصحّحه ويسجّل النتيجة، بأمانٍ تامّ، دون أن يجمّد الخادمَ أو يخترقه. هنا يلتقي كلُّ الستاك الذي تعلّمته بـ أقوى مهارتيك: C و Docker. ننزل للإقليم ١٠، الكابستون — حيث تكتشف أن المنصّةَ كلَّها وُجدت لتُنسّق شيئاً... كنتَ تعرفه من البداية.