المعمارية English

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 &amp; 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 أصلًا
القائد التقني · أمير هارون مسودة v0.1 · بحث وتصميم فقط