Files
peikarband/docs/architecture/database-strategy.md
Ehsan.Asadi 8a924f6091
Some checks failed
CD - Build & Deploy / build-and-push (push) Has been cancelled
CD - Build & Deploy / package-helm (push) Has been cancelled
CD - Build & Deploy / deploy-staging (push) Has been cancelled
CD - Build & Deploy / deploy-production (push) Has been cancelled
CD - Build & Deploy / release (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / security (push) Has been cancelled
[INIT-001] Initial project setup with Clean Architecture (feat)
- Implemented Clean Architecture with Domain, Application, Infrastructure, Presentation layers
- Added comprehensive project structure following SOLID principles
- Created Kubernetes deployment with Helm charts (HPA, PDB, NetworkPolicy)
- Configured ArgoCD for automated deployment (production + staging)
- Implemented CI/CD pipeline with GitHub Actions
- Added comprehensive documentation (handbook, architecture, coding standards)
- Configured PostgreSQL, Redis, Celery for backend services
- Created modern landing page with Persian fonts (Vazirmatn)
- Added Docker multi-stage build for production
- Configured development tools (pytest, black, flake8, mypy, isort)
- Added pre-commit hooks for code quality
- Implemented Makefile for common operations
2025-12-26 15:52:50 +03:30

11 KiB
Raw Blame History

استراتژی دیتابیس - انعطاف و مقیاس‌پذیری

مشکل: انعطاف vs ثبات

با توجه به اینکه پروژه قرار است:

  • کلی feature جدید بگیره
  • Options و configurations متنوع داشته باشه
  • قابل توسعه سریع باشه

آیا PostgreSQL محدودیت ایجاد می‌کنه؟

راه حل: Hybrid Database Architecture

لایه‌بندی داده بر اساس نوع:

┌──────────────────────────────────────────────┐
│            Application Layer                  │
└────┬─────────────┬──────────────┬────────────┘
     │             │              │
┌────▼─────┐  ┌───▼────┐    ┌───▼────┐
│PostgreSQL│  │MongoDB │    │ Redis  │
│          │  │        │    │        │
│Critical  │  │Flexible│    │ Cache  │
│Structured│  │Dynamic │    │Session │
└──────────┘  └────────┘    └────────┘

Tier 1: PostgreSQL - Critical Business Data

چی بریزیم توی PostgreSQL:

  • User accounts
  • Financial data (invoices, transactions, wallet)
  • Service subscriptions
  • Audit logs
  • Anything که نیاز به ACID داره
# ❌ این‌ها رو NEVER توی MongoDB نذاریم
class Invoice(BaseModel):
    id: int
    user_id: int
    amount: Decimal  # MUST be ACID
    status: str
    paid_at: datetime

چرا PostgreSQL؟

  • ACID Transactions
  • Data Integrity
  • Complex Joins
  • Proven for financial data

Tier 2: PostgreSQL JSONB - Flexible Structured

چی بریزیم توی JSONB:

  • Service configurations
  • User preferences
  • Feature flags
  • Custom metadata
  • Plugin settings
from sqlalchemy.dialects.postgresql import JSONB

class Service(BaseModel):
    # Structured
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer)
    type = Column(String)  # 'vps', 'hosting', etc.
    
    # Flexible - هر چیزی می‌تونه باشه!
    config = Column(JSONB, default={})
    metadata = Column(JSONB, default={})

# Example 1: VPS
vps = Service(
    type="vps",
    config={
        "cpu": 4,
        "ram": 8,
        "disk": 100,
        "os": "ubuntu-22.04"
    }
)

# Example 2: WordPress Hosting
wp = Service(
    type="wordpress",
    config={
        "domain": "example.com",
        "php_version": "8.2",
        "auto_update": True,
        "cdn_enabled": True,
        # Future features - بدون migration!
        "new_feature_2025": {"enabled": True}
    }
)

Query کردن JSONB:

# پیدا کردن VPS های بالای 4 CPU
services = session.query(Service).filter(
    Service.config['cpu'].astext.cast(Integer) >= 4
).all()

# پیدا کردن WordPress با CDN enabled
services = session.query(Service).filter(
    Service.config['cdn_enabled'].astext == 'true'
).all()

# Index روی JSONB برای performance
from sqlalchemy import Index

Index(
    'idx_service_config_cpu',
    Service.config['cpu'].astext.cast(Integer)
)

Tier 3: MongoDB - Completely Dynamic

چی بریزیم توی MongoDB:

  • Logs و events
  • Analytics data
  • User activity tracking
  • System metrics
  • Unstructured data
# MongoDB - No schema!
{
    "_id": "service_log_123",
    "service_id": 123,
    "timestamp": "2025-01-24T...",
    "events": [
        {
            "type": "cpu_spike",
            "value": 95,
            "custom_field_1": "...",
            # هر چیزی که بخوایم!
        }
    ],
    "metrics": {
        # Structure آزاد
        "anything": "goes here"
    }
}

پیاده‌سازی در کد

1. Service با JSONB:

# src/infrastructure/database/models/service_model.py

from sqlalchemy import Column, Integer, String, ForeignKey, Enum as SQLEnum
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import relationship
import enum

class ServiceType(enum.Enum):
    VPS = "vps"
    HOSTING = "hosting"
    WORDPRESS = "wordpress"
    DOMAIN = "domain"

class ServiceModel(BaseModel):
    __tablename__ = "services"
    
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('users.id'), nullable=False)
    type = Column(SQLEnum(ServiceType), nullable=False)
    status = Column(String(20), nullable=False)
    
    # Flexible configuration
    config = Column(JSONB, nullable=False, default={})
    metadata = Column(JSONB, nullable=False, default={})
    
    # Relationships
    user = relationship("UserModel", back_populates="services")

2. Repository با JSONB support:

# src/infrastructure/database/repositories/service_repository.py

from typing import Dict, Any, List
from sqlalchemy.orm import Session

class ServiceRepository:
    def __init__(self, session: Session):
        self._session = session
    
    def find_by_config(
        self,
        json_path: str,
        value: Any
    ) -> List[ServiceModel]:
        """Query by JSONB field.
        
        Example:
            repo.find_by_config('cpu', 4)
            repo.find_by_config('cdn_enabled', True)
        """
        return self._session.query(ServiceModel).filter(
            ServiceModel.config[json_path].astext == str(value)
        ).all()
    
    def update_config(
        self,
        service_id: int,
        config_updates: Dict[str, Any]
    ) -> ServiceModel:
        """Update service config partially."""
        service = self.get_by_id(service_id)
        if service:
            # Merge configs
            new_config = {**service.config, **config_updates}
            service.config = new_config
            self._session.commit()
        return service

3. Domain Entity که flexible هست:

# src/core/domain/entities/service.py

from typing import Dict, Any, Optional
from src.core.domain.entities.base import BaseEntity

class Service(BaseEntity):
    """Service domain entity with flexible configuration."""
    
    def __init__(
        self,
        id: Optional[int] = None,
        user_id: int = None,
        type: str = None,
        status: str = None,
        config: Dict[str, Any] = None,
        metadata: Dict[str, Any] = None,
        **kwargs
    ):
        super().__init__(id=id, **kwargs)
        self.user_id = user_id
        self.type = type
        self.status = status
        self.config = config or {}
        self.metadata = metadata or {}
    
    def get_config(self, key: str, default: Any = None) -> Any:
        """Get config value safely."""
        return self.config.get(key, default)
    
    def set_config(self, key: str, value: Any) -> None:
        """Set config value."""
        self.config[key] = value
    
    def update_config(self, updates: Dict[str, Any]) -> None:
        """Update multiple config values."""
        self.config.update(updates)

مقایسه روش‌ها

Migration-based (Traditional SQL)

# ❌ هر feature جدید = migration جدید

# Migration 001
ALTER TABLE services ADD COLUMN cpu INTEGER;

# Migration 002  
ALTER TABLE services ADD COLUMN ram INTEGER;

# Migration 003
ALTER TABLE services ADD COLUMN new_feature VARCHAR(255);

# بعد از 100 feature = 100 migration! 😱

JSONB-based (Flexible SQL)

# ✅ هیچ migration لازم نیست!

# روز اول
service.config = {"cpu": 4}

# یک ماه بعد
service.config = {"cpu": 4, "ram": 8}

# یک سال بعد  
service.config = {
    "cpu": 4,
    "ram": 8,
    "new_feature_2025": True,
    "another_feature": {"nested": "data"}
}

# بدون هیچ migration! 🎉

Best Practices

1. تصمیم‌گیری: PostgreSQL vs MongoDB vs JSONB

# PostgreSQL (Structured)
 User authentication
 Financial transactions
 Invoices
 Core business entities

# PostgreSQL JSONB
 Service configurations
 User preferences
 Feature flags
 Plugin settings
 Custom fields

# MongoDB
 Logs & Events
 Analytics
 User activity
 System metrics
 Temporary data

2. JSONB Schema Validation (در Application)

from pydantic import BaseModel
from typing import Optional

class VPSConfig(BaseModel):
    """Schema for VPS service config."""
    cpu: int
    ram: int
    disk: int
    os: str
    backups: Optional[bool] = False
    
class WordPressConfig(BaseModel):
    """Schema for WordPress service config."""
    domain: str
    php_version: str
    auto_update: bool
    cdn_enabled: Optional[bool] = False

# Validation
def validate_service_config(service_type: str, config: dict):
    """Validate config based on service type."""
    schemas = {
        "vps": VPSConfig,
        "wordpress": WordPressConfig,
    }
    schema = schemas.get(service_type)
    if schema:
        return schema(**config)  # Validates
    return config

3. Indexing برای Performance

# در migration
from alembic import op

def upgrade():
    # Create GIN index on JSONB
    op.execute("""
        CREATE INDEX idx_services_config_gin 
        ON services USING GIN (config);
    """)
    
    # Index specific keys
    op.execute("""
        CREATE INDEX idx_services_config_cpu 
        ON services ((config->>'cpu'));
    """)

مزایا و معایب

PostgreSQL + JSONB (پیشنهاد من)

مزایا:

  • انعطاف بالا (مثل MongoDB)
  • ACID transactions (برای billing امن)
  • Query قدرتمند
  • یک database کمتر = ساده‌تر
  • Performance خوب با indexing
  • Data integrity

معایب:

  • JSONB query ها کمی پیچیده‌تر از SQL معمولی
  • نیاز به validation در application layer

Hybrid (PostgreSQL + MongoDB)

مزایا:

  • Best of both worlds
  • Separation of concerns
  • Optimal performance

معایب:

  • پیچیدگی بیشتر
  • دو database = maintenance بیشتر
  • Consistency بین دو DB

نتیجه‌گیری

پیشنهاد برای پیکربند:

"""
Tier 1: PostgreSQL - Critical
  - Users, Auth
  - Invoices, Transactions
  - Wallet, Payments
  
Tier 2: PostgreSQL JSONB - Flexible
  - Service configs
  - User preferences
  - Custom settings
  
Tier 3: Redis - Cache
  - Sessions
  - Cache
  - Rate limiting

(Optional) Tier 4: MongoDB - Logs
  - Activity logs
  - System metrics
  - Analytics
"""

در عمل:

  • شروع با PostgreSQL + JSONB
  • اگر لازم شد، MongoDB اضافه می‌کنیم
  • ساده، flexible، و قابل توسعه

PostgreSQL دست و پای ما رو نمی‌بنده، اگر از JSONB استفاده کنیم!