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

454 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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