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
454 lines
11 KiB
Markdown
454 lines
11 KiB
Markdown
# استراتژی دیتابیس - انعطاف و مقیاسپذیری
|
||
|
||
## مشکل: انعطاف 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 داره
|
||
|
||
```python
|
||
# ❌ اینها رو 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
|
||
|
||
```python
|
||
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:**
|
||
|
||
```python
|
||
# پیدا کردن 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
|
||
|
||
```python
|
||
# 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:
|
||
|
||
```python
|
||
# 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:
|
||
|
||
```python
|
||
# 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 هست:
|
||
|
||
```python
|
||
# 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)
|
||
|
||
```python
|
||
# ❌ هر 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)
|
||
|
||
```python
|
||
# ✅ هیچ 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
|
||
|
||
```python
|
||
# 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)
|
||
|
||
```python
|
||
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
|
||
|
||
```python
|
||
# در 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
|
||
|
||
## نتیجهگیری
|
||
|
||
**پیشنهاد برای پیکربند:**
|
||
|
||
```python
|
||
"""
|
||
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 استفاده کنیم!** ✨
|
||
|