نُطقي — البنية المعمارية
كيف يتركّب نُطقي: التطبيقات والحزم المشتركة، والمنظومة التقنية، والاصطلاحات العامة للمنصة، واستراتيجية ثنائية اللغة والاتجاه من اليمين إلى اليسار.
المنظومة في لمحة#
تخطيط المستودع الموحّد#
nutqi/
apps/
web/ Next.js 15 App Router (ar RTL default + en), Tailwind v4, next-intl
api/ NestJS 11 + Prisma + PostgreSQL 16, REST /api/v1
docs/ this documentation site (statically rendered markdown)
packages/
ui/ design tokens + shared React components (Button, Card, StatusChip, …)
shared/ TypeScript types + enums + zod schemas shared web↔api
specs/ implementation specs (source of truth for the build)
docs-content/ markdown sources rendered by the docs site
مساحة عمل واحدة بـ pnpm يقودها Turborepo، فيمرّ كل تطبيق وكل حزمة بالبناء والفحص والاختبار عبر خط الأنابيب نفسه.
المنظومة التقنية#
| الطبقة | الاختيار |
|---|---|
| الويب | Next.js 15 (بموجّه App Router)، و Tailwind CSS v4، و next-intl — العربية (من اليمين إلى اليسار) هي الافتراضية، والإنجليزية ثانوية |
| الواجهة الخلفية | NestJS 11، وواجهة REST تحت /api/v1، وكائنات نقل مُتحقَّق منها، ورسائل خطأ ثنائية اللغة |
| البيانات | PostgreSQL 16 مع Prisma؛ ومعرّفات من نوع cuid؛ وأعمدة تدقيق في كل جدول |
| المصادقة | جلسات آمنة عبر كوكيز httpOnly؛ وتحكّم في الوصول قائم على الأدوار مع فحوص ملكية على مستوى الصف |
| المظهر | خصائص CSS مخصّصة في @nutqi/ui؛ مظهران فاتح وداكن، والفاتح هو الافتراضي |
| التوثيق | موقع ثابت بـ Next.js 15 يعرض ملفات markdown من المستودع؛ ومخططات Mermaid |
الاصطلاحات العامة للمنصة#
- المبالغ المالية تُخزَّن أعداداً صحيحة بالقروش (الجنيه المصري × 100) وتُنسَّق عبر أداة مشتركة — فلا وجود لأي عملة بفاصلة عائمة في أي موضع.
- الطوابع الزمنية تُخزَّن بتوقيت
UTCوتُعرض بتوقيتAfrica/Cairo. - الحذف الناعم (
deletedAt) يحفظ الملاحظات وقوالب الخطط ومدخلات معلومات العمل وإعلانات الوظائف. - كل جدول يحمل
createdAt/updatedAt؛ والمعرّفات من نوعcuid. - الملفات المرفوعة تُقدَّم عبر الواجهة الخلفية (
/api/v1/files/:id) مع طبقة تخزين مُجرَّدة تمهيداً للتخزين السحابي الكائني. - الزمن الحقيقي: الأحداث المُرسَلة من الخادم تغذّي شارة الإشعارات داخل التطبيق.
ثنائية اللغة والاتجاه من اليمين إلى اليسار#
- العربية هي اللغة الافتراضية وتُعرض من اليمين إلى اليسار؛ والإنجليزية لغة ثانوية تُعرض من اليسار إلى اليمين. ورسائل التحقق وأخطاء الواجهة الخلفية ثنائية اللغة (
message+messageAr، احتراماً لترويسةAccept-Language). - التخطيطات تستعمل خصائص
CSSالمنطقية في كل موضع، فينعكس كل مكوّن تلقائياً حين ينقلب الاتجاه. - يصدر هذا التوثيق في نسختين كاملتين غير مختلطتين — عربية (من اليمين إلى اليسار) وإنجليزية (من اليسار إلى اليمين) — فلا يلتقي القارئ بالخطّين في جملة واحدة. والاتجاه يأتي من
<html dir>وترثه كل الكتل؛ ولا تُترك الكتل عمداً لتستنبط اتجاهها بنفسها، لأن ذلك الاستنباط يقوم على أول محرف قويّ فيقلب فقرةً عربيةً صادف أن بدأت بمعرّف أو برقم. ولا يُعزل إلا ما كان بالخط الغريب عن النسخة — اللاتيني داخل النثر العربي، والعربي داخل النثر الإنجليزي — فتبقى علامات الترقيم المحيطة به ملتصقةً بالجملة المضيفة. والمسرد هو الصفحة الوحيدة التي يظهر فيها الخطّان جنباً إلى جنب، في عمودين منفصلين، وخلاياه هي الموضع الوحيد الذي ما زال يستنبط اتجاه كل خلية على حدة.
المظهر#
- مظهران، فاتح وداكن، والفاتح هو الافتراضي: فالزائر لأول مرة يحصل على الفاتح دائماً، ولو كان نظام تشغيله مضبوطاً على الداكن. ولا تُطبَّق تفضيلات النظام تلقائياً بحال — فلا يبدّل المظهرَ إلا اختيار صريح.
- المظهر النشط سمة واحدة على العنصر الجذر (
data-theme)، ويُحفظ الاختيار لكل جهاز في تخزين المتصفح. ويطبّقه نصّ صغير قبل أول رسم للصفحة، فلا يرى القارئ العائد إلى الوضع الداكن وميضاً أبيض. - اللون يعيش في طبقة رموز دلالية واحدة: فسلالم الهوية البصرية لا تتغيّر بين المظهرين، ولا ينقلب إلا ما بُني فوقها من معانٍ — خلفية الصفحة، والأسطح، والحدود، ونص التمييز، ودرجات شارات الحالة، وألوان سلاسل الرسوم البيانية، والظلال. والمكوّنات تشير إلى المعاني لا إلى لون خام قط، وهذا بالضبط ما يجعل سمةً واحدةً تعيد طلاء المنتج كله.
- ويستعمل المنتج وموقع التوثيق السمة نفسها والتفضيل المحفوظ نفسه، فيتعرّف كلٌّ منهما على التبديل الذي جرى في الآخر.
الوضع الأمني#
- في المتصفح: سياسة أمن محتوى نافذة تحمل قيمة تعمية طازجة لكل طلب، فلا يعمل من النصوص البرمجية إلا ما ضمنه الخادم؛ ولا تُمنح
unsafe-evalبحال. ومعهاHSTS، وترويسات عزل المصادر المتقاطعة، وnosniff، وسياسة تمنع التأطير، وبلا لافتة لإطار العمل. - في الواجهة الخلفية: يُتحقَّق من كل متن طلب تحققاً صارماً وتُرفض الحقول غير المعروفة رفضاً قاطعاً، ويُحدّ حجم المتن، ويُحدّ معدّل الطلبات لكل عنوان شبكة بميزانية أضيق بكثير على مسارَي تسجيل الدخول ورمز التأكيد. وتوثيق الواجهة معطّل في الإنتاج ما لم يُفعَّل صراحةً.
- عند الإقلاع: غياب مفتاح التوقيع أو فراغ قائمة المصادر المسموحة يرفض الإقلاع في الإنتاج بدل الارتداد إلى قيمة افتراضية غير آمنة، فيفشل النشر الخاطئ بصوتٍ عالٍ بدل أن يعمل مفتوحاً.
- الصحة: يخدم
GET /api/v1/healthالوسيط العكسي وفحص صحة الحاوية — فحص حياة وفحص قاعدة بيانات حقيقي، وخالٍ عمداً من أرقام الإصدارات وسلاسل الاتصال ونصوص الأخطاء.
النشر#
- ينشر تطبيق الويب وموقع التوثيق على
Vercelمن المستودع: رابط معاينة لكل طلب دمج، والإنتاج منmain. - وتعمل الواجهة الخلفية على مضيف
DockerخلفNginxلا على منصة بلا خواديم، لأنها تمسك اتصالات قاعدة بيانات طويلة العمر، وتكتب الملفات المرفوعة على القرص، وتشغّل الترحيلات عند الإصدار. وتعيش العدّة فيdeploy/: حزمةCompose(الواجهة وPostgreSQL، مع فحوص صحة وخطوة ترحيل تُنفَّذ مرةً واحدة)، وقالبTLSلـNginx، ونصوص التهيئة والنشر والتراجع، ودليل تشغيل يغطي النسخ الاحتياطي والاستعادة وتجديد الشهادات. - ومنفذ الواجهة يرتبط بالمضيف المحلي وحده، و
Nginxهو السطح العام الوحيد.
للمطوّرين
التطوير المحلي (portless)#
كل خادم تطوير يعمل من خلال portless، وهو يمنح أسماء مضيفين محلية نظيفة بـ HTTPS ويحقن المتغيّر PORT — فلا يثبّت أي سكربت رقم منفذ (مثال: portless nutqi-docs -- pnpm --filter @nutqi/docs dev).
| الخدمة | اسم المضيف | ملاحظات |
|---|---|---|
| الويب | https://nutqi-web.localhost | خادم التطوير الخاص بـ Next.js |
| الواجهة الخلفية | https://nutqi-api.localhost | واجهة Swagger على /api/docs |
| التوثيق | https://nutqi-docs.localhost | هذا الموقع |
- يُبقي تطبيق الويب نداءات المتصفح على الأصل نفسه عبر إعادة كتابة في
Next.js:/api/:path*←${API_URL}/api/:path*، فتعمل المصادقة بكوكيزhttpOnlyرغم اختلاف أسماء المضيفين في بيئة التطوير. والمتغيّرAPI_URLيُوصَّل منportless get nutqi-api. - تضبط الواجهة الخلفية المتغيّر
CORS_ORIGINليشمل اسم مضيف الويب. - قواعد البيانات:
nutqi_devللتطوير، وnutqi_testللاختبارات. - البناء لا يزعج خادم تطوير يعمل أبداً. فكلا تطبيقَي
Nextيثبّت خادم التطوير على مجلد إخراج خاص به (NEXT_DIST_DIR=.next-devفي سكربتdev)، ويترك.nextللبناء وحده. فمشاركة مجلد واحد كانت تعني أنpnpm buildيطمس القطع التي يقدّمها خادم تطوير حيّ، فيفشل كل طلب بعدها بخطأMODULE_NOT_FOUNDعلى قطعةwebpack— وهو خطأ يُقرأ كعلة في الشيفرة وليس كذلك. وفصل التطوير لا البناء يُبقي مسار البناء مطابقاً حرفياً للتكامل المستمر ولـVercel، فيصير السلوك الآمن هو الافتراضي بلا رايةٍ تُتذكَّر.
خط أنابيب التكامل المستمر (GitHub Actions)#
يعمل .github/workflows/ci.yml عند الدفع إلى main وعند كل طلب دمج (وتُلغى التشغيلات التي تجاوزتها تشغيلات أحدث). وتعمل سبع مهامّ على التوازي، تعلن كل واحدة أدنى صلاحيات تحتاجها وتحمل مهلةً زمنية:
- البوابات —
pnpmوNodeمع تخزين مؤقت للاعتماديات ولـTurborepo، وحاوية خدمةPostgreSQL 16يُعِدّهاprisma migrate deploy، ثمlint←typecheck←test←build، وكل واحدة تتفرّع على جميع حزم مساحة العمل. - الأسرار — مسح أسرار المستودع.
- الاعتماديات — تدقيق الثغرات وفحص سياسة التراخيص.
- التحليل الساكن — فحص الشيفرة.
- مراجعة الاعتماديات — على كل طلب دمج.
- الحاوية — تبني صورة الواجهة الخلفية، وتُصدر قائمة مكوّنات البرمجية، وتفشل عند الثغرات العالية أو الحرجة.
- الترحيلات — تلتقط انحراف المخطط قبل وصوله إلى أي بيئة.
ولا تعمل مهمة النشر الإنتاجي إلا على main بعد نجاح البوابات. أما النشرات التمهيدية فتأتي من تكامل Vercel مع المستودع، فلكل طلب دمج رابط يضغطه المراجع.
ويُتوقَّع أن تمرّ البوابات الأربع نفسها محلياً من جذر المستودع قبل الدفع.
بوابات جودة أخرى غير الأربع#
ثمة ثوابت لا يستطيع المراجع أن يتحقّق منها بعينه على نحو موثوق، فتُفرَض بنصوص برمجية واختبارات بدلاً من ذلك:
| البوابة | ما تفرضه |
|---|---|
check-logical.mjs | ألا توجد أدوات اتجاه فيزيائي (pl/pr، ml/mr، left/right) في مصادر الويب أو واجهة المستخدم، فينعكس الاتجاه تلقائياً |
check-i18n.mjs | تطابق مفاتيح ar وen في كل نطاق، وألا توجد قيم فارغة، وألا تبقى عربية في نص إنجليزي، وألا يوجد نسخ ولصق بلا ترجمة |
check-literal-colors.mjs | ألا توجد ألوان حرفية في المكوّنات — فكل شيء يمرّ عبر طبقة الرموز الدلالية، وإلا انكسر المظهر الداكن صامتاً |
no-mixing.test.ts | أن تكون كل نسخة من التوثيق أحادية الخط، مع تأكيد المسرد استثناءً مقصوداً |
direction.test.ts | ألا يُعزل إلا ما كان بالخط الغريب، وأن تُترك كتل الشيفرة وشأنها |
smoke.test.ts | أن يتطابق هيكل العناوين بين النسختين، وأن يحمل سجل القرارات المعرّفات نفسها بالترتيب نفسه |
كيف يعمل موقع التوثيق هذا#
- التطبيق
apps/docsتطبيقNext.js 15ثابت بالكامل. وهو يقرأ عند البناء ملفاتmarkdownمباشرةً منdocs-content/وspecs/02-api.md— فالمصادر لا تُنسخ ولا تُشتَقّ، ويبقىmarkdownالمستودع هو المرجع الوحيد. - المخططات تُكتب في كتل شفرة من نوع
mermaidوتُحوَّل إلىSVGداخل المتصفح بواسطةMermaid 11(مكوّن عميل يُحمَّل عند الطلب). - فهرس البحث يُولَّد عند البناء من المصادر نفسها ويُقدَّم بصيغة
JSONثابتة. - صفحة البنية المعمارية (
apps/docs/content/architecture.md) وهذا الملحق هما المحتوى الوحيد المكتوب داخل تطبيق التوثيق نفسه.