404 Not Found

404 Not Found


nginx

تصميم المشروع — مخطط معمارية PriceTracker الكاملة

تصميم المشروع كمخطط قبل بناء مبنى — إذا بدأت البناء بدون مخطط، قد تجد أن الأساس ليس قويًا كفاية عندما تصل للطابق الثالث، ولن يكون أمامك خيار سوى الهدم والبدء من جديد. المخطط الجيد ليس شيئًا ترسمه ثم تتجاهله؛ بل يظل دليلًا طوال عملية البناء بالكامل.

1. ما ستتعلمه


2. القصة الحقيقية لـ Alice

(1) المشكلة: كلما زادت الميزات، زاد الفوضى

تطور PriceTracker من API بسيط إلى خدمة بعدد متزايد من الميزات، لكنه كان يفتقر لتصميم موحد — حدود المنتجات والأسعار غير واضحة، منطق الاشتراك مبعثر عبر 10 نقاط نهاية، ونظام الإشعارات مقترن بإحكام مع منطق الأعمال. كلما أُضيفت ميزة جديدة، كان يجب تعديل خمسة ملفات، وأخطاء الارتداد تحدث بشكل متكرر.

(2) حل التصميم القائم على المجال

يُحدد التصميم القائم على المجال (DDD) المجالات الأساسية والحدود من منظور الأعمال: تجميع المنتج، وتجميع السعر، وتجميع المستخدم، وتجميع الاشتراك — لكل تجميع حدود ومسؤوليات واضحة، والتواصل عبر التجميعات يتم عبر الأحداث، مما يُلغي الترابط.

(3) العائد

لإضافة ميزات جديدة، تحتاج فقط لتعديل الكود ضمن التجميع المقابل؛ التغييرات لم تعد تؤثر على النظام بالكامل. منطق الاشتراك مركز في تجميع Subscription، والإشعارات تُطلَّق عبر حدث PriceUpdated، مما يضمن فكًا كاملاً.


3. تحليل المتطلبات

(1) حالات الاستخدام الأساسية

الدور حالة الاستخدام الأولوية
مستخدم (Alice) تسجيل/تسجيل الدخول P0
مستخدم عرض سعر المنتج P0
مستخدم اشتراك إشعار تغير السعر P1
مسؤول استيراد أسعار دفعي P0
مسؤول إدارة CRUD للمنتجات P0
واجهة أمامية (Bob) استدعاء API لاسترجاع البيانات P0
DevOps (Charlie) مراقبة صحة النظام P1
النظام جمع أسعار تلقائي P2

(1) ▶ مثال: نص لترتيب حالات الاستخدام حسب الأولوية

PYTHON
# scripts/prioritize_use_cases.py
use_cases = [
    {"role": "User", "action": "register_login", "priority": "P0", "effort": 2},
    {"role": "User", "action": "query_price", "priority": "P0", "effort": 1},
    {"role": "User", "action": "subscribe_alert", "priority": "P1", "effort": 3},
    {"role": "Admin", "action": "bulk_import", "priority": "P0", "effort": 5},
]

# ترتيب حسب الأولوية؛ ضمن نفس المستوى، ترتيب تصاعدي حسب حجم العمل
order = {"P0": 0, "P1": 1, "P2": 2}
sorted_cases = sorted(use_cases, key=lambda x: (order[x["priority"]], x["effort"]))
for uc in sorted_cases:
    print(f"[{uc['priority']}] {uc['role']}: {uc['action']} (effort={uc['effort']}d)")

الناتج:

TEXT
# تم التنفيذ بنجاح

(2) المتطلبات غير الوظيفية

المتطلب الهدف القيود
QPS ملايين (مجموعة) Nginx LB + مرونة K8s
زمن الاستجابة P99 < 50 ms تخزين مؤقت Redis + DB غير متزامن
التوفر 99.9% نسخ متعددة + استرداد تلقائي
حجم البيانات ملايين منتجات + عشرات ملايين أسعار تقسيم PostgreSQL

4. تصميم نموذج المجال

(1) مخطط ER

100%
erDiagram
    User ||--o{ Subscription : has
    User ||--o{ Product : creates
    User ||--o{ Alert : sets
    Product ||--o{ Price : has
    Product ||--o{ Alert : watched_by
    Subscription ||--o{ Feature : includes
    
    User {
        int id PK
        string email UK
        string hashed_password
        string role
        datetime created_at
    }
    
    Product {
        int id PK
        string name
        string category
        float base_price
        string sku UK
        int user_id FK
        datetime created_at
    }
    
    Price {
        int id PK
        int product_id FK
        float price
        string currency
        string source
        datetime recorded_at
    }
    
    Subscription {
        int id PK
        int user_id FK
        string plan
        datetime starts_at
        datetime expires_at
    }
    
    Alert {
        int id PK
        int user_id FK
        int product_id FK
        float target_price
        string status
    }

(2) تحديد جذور التجميع

جذر التجميع الكيانات المضمنة قواعد الحدود
User المستخدم، الاشتراك، التنبيه المستخدم يمتلك الاشتراكات والتنبيهات
Product المنتج، السعر المنتج لديه سجل أسعار
PriceImport معلومات الدفعة، حالة الاستيراد الاستيراد الدفعي معاملة منفصلة

(1) ▶ مثال: تعريفات أحداث المجال

PYTHON
# domain/events.py
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum

class EventType(Enum):
    PRICE_CHANGED = "price_changed"
    ALERT_TRIGGERED = "alert_triggered"
    USER_SUBSCRIBED = "user_subscribed"

@dataclass
class DomainEvent:
    event_type: EventType
    aggregate_id: int
    occurred_at: datetime = field(default_factory=datetime.utcnow)
    payload: dict = field(default_factory=dict)

# الاستخدام: أحداث تغير السعر
price_event = DomainEvent(
    event_type=EventType.PRICE_CHANGED,
    aggregate_id=42,
    payload={"old_price": 299.9, "new_price": 249.9, "currency": "CNY"},
)

الناتج:

TEXT
# تم التنفيذ بنجاح

5. قرارات الاختيار التقني

(1) مقارنة النماذج

الحاجة الخيار أ الخيار ب القرار السبب
أطر الويب FastAPI Flask/Django FastAPI غير متزامن + توثيق تلقائي + فحص أنواع
قاعدة البيانات PostgreSQL MySQL/MongoDB PostgreSQL JSONB + مشغل غير متزامن ناضج
التخزين المؤقت Redis Memcached Redis هياكل بيانات غنية + استمرار
طابور المهام Celery+Redis RQ/Dramatiq Celery نظام بيئي ناضج + مراقبة قوية
الاتصال في الوقت الفعلي WebSocket SSE WebSocket ثنائي الاتجاه + زمن استجابة منخفض
الحاويات Docker bare metal/VM Docker تناسق + تنسيق

(2) نظرة عامة على معمارية النظام

100%
flowchart TD
    Client[العميل / Bob الواجهة الأمامية] --> Nginx[Nginx موازن تحميل]
    Nginx --> API1[FastAPI Pod 1]
    Nginx --> API2[FastAPI Pod 2]
    Nginx --> APIN[FastAPI Pod N]
    
    API1 --> PG_Master[(PostgreSQL الرئيسي)]
    API2 --> PG_Master
    APIN --> PG_Master
    
    PG_Master --> PG_Replica[(PostgreSQL النسخة)]
    
    API1 --> Redis[(مجموعة Redis)]
    API2 --> Redis
    APIN --> Redis
    
    Redis --> CW1[Celery Worker 1]
    Redis --> CW2[Celery Worker 2]
    
    CW1 --> PG_Master
    CW2 --> PG_Master
    
    API1 --> WS[WebSocket Hub]
    API2 --> WS
    
    Prometheus[Prometheus] --> API1
    Grafana[Grafana] --> Prometheus

6. استراتيجية إصدارات API

(1) إصدار URL مقابل إصدار Header

البُعد إصدار URL /api/v1/ إصدار Header Accept: v=1
الوضوح عالٍ (URL صريح) منخفض (مخفي في الـ header)
التوجيه بسيط (قائم على البادئة) معقد (يتطلب تحليل middleware)
التخزين المؤقت تخزين مؤقت URL مستقل يتطلب Vary Header
Swagger تجميع تلقائي يتطلب إعداد يدوي
مُوصى به ✅ كيف يستخدم PriceTracker مناسب لـ APIs الداخلية

(1) ▶ مثال: هيكل توجيه إصدار URL

PYTHON
from fastapi import APIRouter

# مسارات V1
v1_router = APIRouter(prefix="/api/v1", tags=["v1"])
v1_products = APIRouter(prefix="/products", tags=["products"])
v1_prices = APIRouter(prefix="/prices", tags=["prices"])
v1_auth = APIRouter(prefix="/auth", tags=["auth"])

# مسارات V2 (مستقبلية)
v2_router = APIRouter(prefix="/api/v2", tags=["v2"])

# تثبيت المسارات
app.include_router(v1_auth)
app.include_router(v1_products, dependencies=[Depends(get_current_user)])
app.include_router(v1_prices, dependencies=[Depends(get_current_user)])
app.include_router(v1_router)

الناتج:

TEXT
# تم التنفيذ بنجاح

❓ أسئلة شائعة

س ما غرض جذر التجميع في DDD؟
ج يُعرّف جذر التجميع حدود المعاملة ويضمن تناسق البيانات. بما أن Product هو جذر التجميع، لا يمكن تعديل Price إلا من خلال Product ولا يمكن إضافته أو حذفه بشكل مستقل، مما يضمن تناسق البيانات.
س كيف تُنفَّذ قراءة/كتابة منفصلة في PostgreSQL؟
ج استخدم قاعدة البيانات الرئيسية للكتابة وقاعدة البيانات الثانوية للقراءة. اضبط محركين في SQLAlchemy (write_engine وread_engine)، واستخدم read_engine للاتصال بقاعدة البيانات الثانوية لعمليات القراءة. ستحتاج لمراعاة زمن النسخ.
س لماذا اختيار Redis بدلاً من Memcached؟
ج Redis يدعم الاستمرار وهياكل بيانات متعددة (Hash/Set/ZSet) وPub/Sub، بينما Memcached يدعم فقط أزواج مفتاح-قيمة بسيطة. Redis يوفر نطاقًا أوسع من الميزات.
س متى تُحدَّث إصدارات API؟
ج تُحدَّث الإصدارات فقط عند وجود تغييرات جذرية (مثل إزالة حقول أو تغيير بنية الاستجابة). إضافة حقول أو نقاط نهاية جديدة لا يُشكل تغييرًا جذريًا ولا يتطلب إصدارًا جديدًا.
س كيف تُنفَّذ أحداث المجال؟
ج نهج بسيط يستخدم مهام Celery (مثل publish_price_updated_event.delay(product_id, new_price))، بينما نهج أكثر تعقيدًا يستخدم طوابير رسائل (RabbitMQ/Kafka).
س ما مدى تفصيل مخطط المعمارية؟
ج للفريق، يحتاج فقط أن يكون "كافيًا". الهندسة الزائدة تضيع الوقت، بينما الهندسة الناقصة تؤدي للارتباك. PriceTracker يحقق التوازن الصحيح مع بنيته ثلاثية المستويات وجذور تجميعه.

📖 ملخص


📝 تمارين

  1. سؤال أساسي (الصعوبة: ⭐): ارسم مخطط معمارية نظام لـ PriceTracker (يشمل FastAPI وPostgreSQL وRedis وCelery وNginx)، وسمِّ مسؤوليات كل مكون وتدفق البيانات. تلميح: مخطط تدفق Mermaid
  2. مسألة متقدمة (الصعوبة ⭐⭐): صمّم مخطط ER لنموذج مجال PriceTracker، حدد ثلاثة جذور تجميع (User، Product، PriceImport)، وعرّف الكيانات المضمنة في كل تجميع وقواعد حدودها. تلميح: مخطط erDiagram Mermaid + جدول جذور التجميع
  3. تحدٍ (الصعوبة: ⭐⭐⭐): أكمِل المخطط المعماري الكامل لـ PriceTracker — بما في ذلك وثيقة المتطلبات (حالات الاستخدام الأساسية + المتطلبات غير الوظيفية)، جدول مقارنة تقنية، كود هيكل توجيه إصدارات API، ومخطط طوبولوجيا النشر (يشمل قواعد بيانات رئيسي-تابع، طبقات تخزين مؤقت، ومجموعة Celery). تلميح: ادمج جميع المحتوى المغطى في هذا الدرس.

---|

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%