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
- 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
11 KiB
11 KiB
استراتژی دیتابیس - انعطاف و مقیاسپذیری
مشکل: انعطاف 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 استفاده کنیم! ✨