arc42 §3 — السياق والنطاق وعرض الحاويات (Container View)
الحالة: مسودة v0.1 · التاريخ: 2026-07-20
يتتبّع إلى: 00-requirements-baseline.md, 01-quality-attribute-scenarios.md
3.1 سياق الأعمال (C4 المستوى 1 — سياق النظام)#
graph TB
subgraph Actors["Human Actors"]
IND["👤 Individual<br/><i>ACT-IND</i><br/>Browses, books, learns"]
CO["🏢 Company Admin<br/><i>ACT-CO</i><br/>Manages employees, buys services"]
EMP["👷 Employee<br/><i>ACT-EMP</i><br/>Learns, takes assessments"]
CON["🎓 Consultant<br/><i>ACT-CON</i><br/>Sets availability, consults"]
SP["🚚 Service Provider<br/><i>ACT-SP</i><br/>Maintains listing, subscribes"]
ADM["🛡️ Platform Admin<br/><i>ACT-ADM</i><br/>Approves, moderates, reports"]
end
QW["<b>Qwizin Platform</b><br/><br/>Operational enablement platform for<br/>KSA restaurant & hospitality sector.<br/>Consultancy · Templates · Training ·<br/>Assessment · Certification · Marketplace"]
subgraph External["External Systems"]
PAY["💳 Payment Gateway<br/><i>UNDECIDED — OQ-01</i><br/>mada, cards, holds, refunds, payouts"]
VID["🎥 Video Provider<br/><i>Behind port — SEAM-02</i><br/>Real-time consultation media"]
GOOG["🔑 Google Identity<br/>Sign-In (OIDC)"]
SMS["📱 SMS Provider<br/>OTP + reminders (KSA)"]
MAIL["✉️ Email Provider<br/>Transactional mail"]
PUSH["🔔 Push Service<br/>APNs / FCM"]
ZATCA["🧾 ZATCA / FATOORA<br/><i>Applicability — OQ-11</i><br/>E-invoicing clearance"]
end
IND & CO & EMP & CON & SP --> QW
ADM --> QW
QW -->|"Charge, refund, pay out"| PAY
QW -->|"Create room, issue token,<br/>receive events"| VID
QW -->|"Verify ID token"| GOOG
QW -->|"Send OTP / reminder"| SMS
QW -->|"Send transactional mail"| MAIL
QW -->|"Deliver push"| PUSH
QW -.->|"Submit / clear invoice"| ZATCA
style QW fill:#1168bd,stroke:#0b4884,color:#fff
style PAY fill:#c94a4a,stroke:#8b2f2f,color:#fff
style ZATCA fill:#c94a4a,stroke:#8b2f2f,color:#fff
style VID fill:#d98c1f,stroke:#96610f,color:#fff
مفتاح الرسم: الأحمر = اعتمادية غير محسومة أو غير مؤكدة تعيق التصميم؛ البرتقالي = مُجرَّدة عن قصد خلف منفذ (port)؛ المتقطّع = قابلية التطبيق غير مؤكدة.
سجل الاعتماديات الخارجية#
| الاعتمادية | الحالة | ما تعيقه | ملاحظات |
|---|---|---|---|
| Payment Gateway | ❌ غير محسومة | OQ-01, OQ-10 |
يجب أن تدعم mada + حجز/تحصيل (authorize/capture) + الاسترداد + payout للسوق الإلكتروني (marketplace). قد تستدعي قدرة الـ payout ترخيصًا من SAMA — وهذه مسألة قانونية لا تقنية. |
| Video Provider | ⚠️ مؤجَّلة عن قصد | SEAM-02 |
مُجرَّدة خلف port بحكم التصميم. قد يُسقط قيد إقامة البيانات (data residency) في CON-13 معظم خدمات CPaaS المُدارة. |
| Google Identity | ✅ مؤكدة | — | SRC-MOM FR-5. يجب التحقق من ID token على الخادم؛ لا تثق بتأكيدات العميل إطلاقًا. |
| SMS Provider | ⚠️ "إن نُفِّذت" | — | SRC-BRD §13 dep 13 يتحفّظ في صياغته. الـ OTP متطلب صارم إن شُحن التسجيل عبر الهاتف. |
| Email Provider | ✅ مؤكدة | — | SRC-BRD §13 dep 12 |
| Push Service | ⚠️ ضمنية | — | غير مذكورة في المصادر، لكن التوجّه mobile-first مع تذكيرات المواعيد يستلزمها |
| ZATCA / FATOORA | ❓ غير مؤكدة | OQ-11 |
إن كانت منطبقة، فهي مسار عمل كبير غير مخطط له. قيد بحث نشط. |
ملاحظة معمارية. ثلاث من الاعتماديات الخارجية السبع غير محسومة، واثنتان منها (المدفوعات، الفوترة الإلكترونية) تنظيمية لا تقنية. هذا أكبر مصدر منفرد لمخاطر الجدول الزمني في البرنامج، وهو غير ظاهر في أي وثيقة مصدر. التخفيف بنيوي: كل اعتمادية غير محسومة توضع خلف port بحيث يمكن اتخاذ القرار متأخرًا دون إعادة تصميم.
3.2 السياق التقني#
| الواجهة | البروتوكول | الاتجاه | ملاحظات |
|---|---|---|---|
| Mobile app → API | HTTPS / REST-JSON, TLS 1.3 | وارد | مُدارة بالإصدارات (/api/v1). Bearer token. العميل الأساسي (CON-16). |
| Web app → API | HTTPS / REST-JSON | وارد | مؤجَّل (mobile-first, web later) — لكن عقد الـ API مصمَّم ليخدمه |
| Admin portal → API | HTTPS | وارد | سطح منفصل، تخويل منفصل، MFA إلزامي (QAS-SEC-04) |
| API → Payment gateway | HTTPS + webhooks | ثنائي الاتجاه | يجب التحقق من توقيع الـ webhooks وأن تكون آمنة التكرار (idempotent) (QAS-AVL-03) |
| API → Video provider | HTTPS + webhooks | ثنائي الاتجاه | دورة حياة الغرفة صادرة، أحداث المشاركين واردة |
| Client → Video media | WebRTC (SRTP/DTLS, ICE) | مباشر | الوسائط لا تمرّ عبر طبقة الـ API. حقيقة طوبولوجية حاسمة — انظر §3.3. |
| API → Object storage | S3-compatible HTTPS | صادر | المستندات، القوالب، الشهادات |
| Client → Object storage | HTTPS, signed URL | مباشر | روابط موقَّعة (signed URL) قصيرة العمر (QAS-SEC-02)؛ تتجاوز الـ API في النقل الكبير |
3.3 عرض الحاويات (C4 المستوى 2)#
وفق CON-14، هذا نمط أحادي معياري (modular monolith) مع خدمات مرافقة (sidecars) مستخرَجة — لا خدمات مصغّرة (microservices). ومعيار الاستخراج المطبَّق ضيّق عن قصد: لا يُفصل مكوّن إلا إذا كان له ملف توسّع أو بيئة تشغيل (runtime) أو نطاق أعطال مختلف فعليًا. وكل ما عدا ذلك يبقى داخل الـ monolith.
graph TB
subgraph Clients["Client Tier"]
MOB["📱 Mobile App<br/><i>iOS / Android</i><br/>Primary client"]
WEB["🌐 Web App<br/><i>Phase 1.5</i>"]
ADMUI["🖥️ Admin Portal<br/><i>Server-rendered</i>"]
end
subgraph Edge["Edge — KSA Region"]
CDN["CDN<br/>Static assets, template files"]
WAF["WAF + DDoS"]
ING["Ingress Controller<br/>TLS termination, routing,<br/>rate limiting"]
end
subgraph K8S["Kubernetes Cluster — In-Kingdom"]
subgraph AppTier["Application Tier — stateless, HPA on RPS/latency"]
API["<b>Qwizin API</b><br/><i>Laravel 13 · PHP 8.4</i><br/>Modular monolith<br/>MOD-IAM · ONB · CONS · TPL ·<br/>LRN · ASMT · CERT · MKT · PAY · ADM"]
end
subgraph WorkerTier["Worker Tier — HPA on queue depth"]
WRK["<b>Queue Workers</b><br/><i>Same image, different entrypoint</i><br/>Notifications, webhooks,<br/>search indexing, reconciliation"]
SCHED["<b>Scheduler</b><br/><i>Singleton</i><br/>Reminders, expiry, subscription<br/>renewal, predictive video scaling"]
end
subgraph Sidecars["Extracted Sidecars — distinct runtime or scaling profile"]
PDF["<b>Document Renderer</b><br/><i>Headless browser runtime</i><br/>Certificates, custom docs<br/><b>Why separate:</b> heavy memory,<br/>different runtime, burst scaling"]
SCAN["<b>Malware Scanner</b><br/><i>ClamAV</i><br/><b>Why separate:</b> large signature<br/>DB, isolation of untrusted input"]
SRCH["<b>Search Engine</b><br/><i>Arabic-capable — TBD</i><br/><b>Why separate:</b> stateful,<br/>own memory/disk profile"]
end
subgraph VideoTier["Video Tier — conditional, see ADR-0005"]
SFU["<b>Media Servers</b><br/><i>Only if self-hosted</i><br/>hostNetwork · session-based scaling"]
TURN["<b>TURN/STUN</b><br/><i>Only if self-hosted</i>"]
end
end
subgraph Data["Data Tier — In-Kingdom"]
DB[("<b>PostgreSQL</b><br/>Primary + read replica<br/>Tenant-scoped")]
REDIS[("<b>Redis</b><br/>Cache · queues · locks<br/>· rate limiting")]
OBJ[("<b>Object Storage</b><br/>S3-compatible<br/>Legal docs · templates ·<br/>certificates<br/><i>Encrypted at rest</i>")]
end
subgraph Obs["Observability"]
OTEL["OpenTelemetry Collector"]
LOGS["Logs · Metrics · Traces<br/>Correlation-ID threaded"]
end
MOB & WEB --> CDN
MOB & WEB & ADMUI --> WAF --> ING
ING --> API
ING -.->|"Deferred: BFF only if<br/>client divergence demands it"| API
API --> DB
API --> REDIS
API --> OBJ
API --> SRCH
WRK --> DB & REDIS & OBJ & SRCH
SCHED --> REDIS
WRK --> PDF
WRK --> SCAN
API -.->|"signed URL"| OBJ
MOB -.->|"direct download<br/>via signed URL"| OBJ
API -->|"port"| VIDEXT["Video Provider<br/><i>managed or self-hosted</i>"]
MOB -.->|"WebRTC media<br/><b>bypasses API</b>"| SFU
SFU --- TURN
API & WRK & PDF --> OTEL --> LOGS
style API fill:#1168bd,stroke:#0b4884,color:#fff
style PDF fill:#2d8659,stroke:#1c5638,color:#fff
style SCAN fill:#2d8659,stroke:#1c5638,color:#fff
style SRCH fill:#2d8659,stroke:#1c5638,color:#fff
style SFU fill:#d98c1f,stroke:#96610f,color:#fff
style TURN fill:#d98c1f,stroke:#96610f,color:#fff
سجل الحاويات#
| الحاوية | بيئة التشغيل (runtime) | إشارة التوسّع | مبرر حدودها |
|---|---|---|---|
| Qwizin API | Laravel 13 / PHP 8.4 | RPS + زمن الاستجابة p95 | الـ monolith. جميع وحدات الأعمال. عديم الحالة (stateless) ← قابل للتوسّع أفقيًا (QAS-SCL-01). |
| Queue Workers | نفس الصورة (image) | عمق الطابور، لا CPU | نفس الكود، مدخل تشغيل مختلف — ليست خدمة منفصلة، وذلك عن قصد. يجب ألّا يشارك العمل غير المتزامن إشارة التوسّع الخاصة بطبقة الطلبات (QAS-SCL-03). |
| Scheduler | نفس الصورة | نسخة واحدة (singleton) | يجب ألّا يعمل في نسخ متعددة. انتخاب قائد (leader election) أو نشر بنسخة واحدة. |
| Document Renderer | متصفح بلا واجهة (headless browser) | عمق طابور المهام | مُستخرَج — بيئة تشغيل المتصفح لها ملف ذاكرة مختلف جذريًا (مئات الميغابايتات لكل عملية توليد) وستشوّه تحجيم pods الخاصة بالـ API. وهو أيضًا المكوّن الوحيد الذي يحتاج حزمة الخطوط والتشكيل العربي (QAS-LOC-01). |
| Malware Scanner | ClamAV | معدل الرفع | مُستخرَج — قاعدة تواقيع كبيرة، وتحديثات دورية، وعزل مقصود: فهو المكوّن الذي يلامس المدخلات غير الموثوقة أولًا (QAS-SEC-03). |
| Search Engine | TBD (يدعم العربية) | حجم الفهرس / QPS | مُستخرَج — ذو حالة (stateful)، وله تخزينه وملف ذاكرته الخاصان. يجب أن يكون قابلًا للاستضافة الذاتية داخل المملكة (CON-13 يُرجَّح أن يُسقط خدمات البحث المُدارة SaaS). |
| Media / TURN | مشروطة | عدد الجلسات، لا CPU | موجودة فقط في خيار الاستضافة الذاتية. متطلب hostNetwork يجعلها معماريًا مختلفة عن كل شيء آخر في الـ cluster — انظر ADR-0005. |
لماذا ليست microservices — ملخص#
مُوثَّق هنا؛ والتحليل الكامل في ADR-0002.
- لا حاجة إلى توسّع مستقل عبر وحدات الأعمال. الحجز والقوالب والتعلّم لها ملفات حِمل شبه متطابقة ومتواضعة. أما المكوّنات التي تختلف فعلًا (توليد PDF، فحص البرمجيات الخبيثة، البحث، الوسائط) فقد استُخرجت بالفعل — وهذه هي الفائدة الهندسية الحقيقية التي تقدّمها microservices، مُحصَّلة دون دفع تكلفة الأنظمة الموزّعة.
- لا مبرر من طوبولوجيا الفرق. حدود الـ microservices تؤتي ثمارها حين تُطابق فرقًا مستقلة. وهنا مورّد تنفيذ واحد.
- المعاملات الموزّعة ستكون خسارة صافية. تدفق الحجز↔الدفع↔الفيديو (
CONF-02) يحتاج أصلًا إدارة حالة دقيقة. وتقسيمه عبر خدمات يحوّل معاملة قاعدة بيانات إلى saga دون أي فائدة. CON-01وQAS-OPS-01. الـ microservices تضاعف السطح التشغيلي الذي يجب أن يديره فريق صغير — بما يعارض مباشرةً قيد التكلفة وسيناريو قابلية التشغيل.- الـ seams محفوظة على أي حال.
QAS-MOD-03يفرض حدودًا مفحوصة في CI، فيبقى استخراج المرحلة 2 رخيصًا. الخيار مُحتفَظ به دون دفع ثمنه الآن.
3.4 حاويات مؤجَّلة عن قصد#
توثيق لما جرى النظر فيه ورُفض في الوقت الحالي، مع المُحفِّز الذي يغيّر القرار:
| المؤجَّل | المبرر | يُعاد النظر عند |
|---|---|---|
| طبقة BFF | عميل واحد فقط عند الإطلاق. الـ BFF بمستهلك واحد قفزة إضافية ووحدة نشر إضافية بلا مكسب. | تباعد احتياجات البيانات بين عميلي الويب والجوال بشكل ظاهر، أو ظهور نوع عميل ثالث |
| API Gateway (بما يتجاوز الـ ingress) | بوابة الدخول (ingress) مع تحديد المعدل داخل التطبيق تغطي الاحتياج الحالي. البوابة المخصصة يبررها وجود خدمات كثيرة، لا خدمة واحدة. | استخراج الوحدات إلى خدمات حقيقية (المرحلة 2) |
| وسيط رسائل (Kafka/RabbitMQ) | الطوابير المبنية على Redis كافية عند هذا الحجم. تجريد ناقل الأحداث (event bus) موجود من اليوم الأول بحيث يمكن استبدال الوسيط دون المساس بالناشرين أو المشتركين (QAS-MOD-03). |
الحاجة إلى أحداث عابرة للخدمات، أو إعادة تشغيل الأحداث، أو تدفقات مرتّبة |
| نموذج قراءة منفصل / CQRS | احتياجات التقارير (SRC-MOM §13 KPIs) يمكن تلبيتها من read replicas. |
بدء الاستعلامات التحليلية في إضعاف أداء المعاملات |
| Service mesh | يضيف تعقيدًا تشغيليًا كبيرًا يعارض QAS-OPS-01. سياسات الشبكة تغطي الحاجة الأمنية. |
خدمات كثيرة باحتياجات mTLS وتشكيل حركة معقدة |
كل صف خيار أُبقي مفتوحًا عن قصد، لا إغفال. والنمط السائد في الوثيقة كلها: ابنِ الـ seam، وأجّل المكوّن.
3.5 بنود مفتوحة تؤثر في هذا العرض#
OQ-01/OQ-10— بوابة الدفع والترخيص؛ تحدّد ما إذا كانت المدفوعات تبقى داخل العملية أم يجب عزلها أكثر لتضييق نطاق الامتثالOQ-08— تسجيل الاستشارات؛ يضيف تخزينًا واحتفاظًا ومسار إخراج للتسجيلات غير مُبيَّن أعلاهOQ-11— انطباق ZATCA؛ سيضيف حاوية أو module للتكامل مع الفوترة- ADR-0005 (بحث معلّق) — الفيديو ذاتي الاستضافة مقابل المُدار يحدّد ما إذا كانت طبقة الفيديو ستوجد داخل الـ cluster أصلًا