[INIT-001] Initial project setup with Clean Architecture (feat)
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
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
This commit is contained in:
386
docs/development/coding-standards.md
Normal file
386
docs/development/coding-standards.md
Normal file
@@ -0,0 +1,386 @@
|
||||
# استانداردهای کدنویسی
|
||||
|
||||
## اصول کلی
|
||||
|
||||
### 1. کد تمیز (Clean Code)
|
||||
|
||||
کد باید:
|
||||
- خوانا باشد
|
||||
- ساده باشد
|
||||
- قابل فهم باشد
|
||||
- قابل نگهداری باشد
|
||||
|
||||
### 2. PEP 8 Compliance
|
||||
|
||||
**الزامی**: تمام کدها باید با PEP 8 مطابقت داشته باشند.
|
||||
|
||||
استفاده از ابزارها:
|
||||
```bash
|
||||
black src/ # Auto-formatting
|
||||
flake8 src/ # Linting
|
||||
isort src/ # Import sorting
|
||||
```
|
||||
|
||||
## قوانین Naming
|
||||
|
||||
### Classes
|
||||
|
||||
```python
|
||||
# ✅ GOOD: PascalCase
|
||||
class UserService:
|
||||
pass
|
||||
|
||||
class PaymentGateway:
|
||||
pass
|
||||
|
||||
class DatabaseConnection:
|
||||
pass
|
||||
|
||||
# ❌ BAD
|
||||
class user_service: # Wrong case
|
||||
class payment_gateway_service: # Too long
|
||||
class UServ: # Unclear abbreviation
|
||||
```
|
||||
|
||||
### Functions & Methods
|
||||
|
||||
```python
|
||||
# ✅ GOOD: snake_case, descriptive
|
||||
def calculate_total_price(items: List[Item]) -> Decimal:
|
||||
pass
|
||||
|
||||
def send_welcome_email(user: User) -> bool:
|
||||
pass
|
||||
|
||||
# ❌ BAD
|
||||
def calcTotPrice(): # Mixed case
|
||||
def cp(): # Not descriptive
|
||||
def calculate(): # Too vague
|
||||
```
|
||||
|
||||
### Variables
|
||||
|
||||
```python
|
||||
# ✅ GOOD
|
||||
user_email = "test@example.com"
|
||||
total_amount = Decimal("100.00")
|
||||
is_active = True
|
||||
MAX_ATTEMPTS = 3 # Constant
|
||||
|
||||
# ❌ BAD
|
||||
e = "test@example.com" # Too short
|
||||
userEmail = "test@example.com" # camelCase
|
||||
TOTAL = 100 # Constant style for variable
|
||||
```
|
||||
|
||||
## Type Hints
|
||||
|
||||
**الزامی**: همه function signatures باید type hints داشته باشند.
|
||||
|
||||
```python
|
||||
from typing import Optional, List, Dict, Any
|
||||
from decimal import Decimal
|
||||
|
||||
# ✅ GOOD
|
||||
def get_user(user_id: int) -> Optional[User]:
|
||||
pass
|
||||
|
||||
def create_invoice(
|
||||
user_id: int,
|
||||
amount: Decimal,
|
||||
items: List[InvoiceItem]
|
||||
) -> Invoice:
|
||||
pass
|
||||
|
||||
# ❌ BAD
|
||||
def get_user(user_id): # No types
|
||||
pass
|
||||
|
||||
def create_invoice(user_id, amount, items): # No types
|
||||
pass
|
||||
```
|
||||
|
||||
## Docstrings
|
||||
|
||||
**الزامی**: همه public functions, classes, و methods باید docstring داشته باشند.
|
||||
|
||||
### Google Style (استاندارد پروژه)
|
||||
|
||||
```python
|
||||
def create_user(
|
||||
email: str,
|
||||
password: str,
|
||||
full_name: str
|
||||
) -> User:
|
||||
"""Create a new user account.
|
||||
|
||||
Args:
|
||||
email: User's email address
|
||||
password: Plain text password
|
||||
full_name: User's full name
|
||||
|
||||
Returns:
|
||||
User: Created user object
|
||||
|
||||
Raises:
|
||||
EmailAlreadyExistsException: If email exists
|
||||
WeakPasswordException: If password is weak
|
||||
|
||||
Example:
|
||||
>>> user = create_user(
|
||||
... email="test@example.com",
|
||||
... password="SecurePass123!",
|
||||
... full_name="John Doe"
|
||||
... )
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Use Specific Exceptions
|
||||
|
||||
```python
|
||||
# ✅ GOOD
|
||||
try:
|
||||
user = user_repository.get_by_id(user_id)
|
||||
if not user:
|
||||
raise UserNotFoundException(f"User {user_id} not found")
|
||||
except DatabaseException as e:
|
||||
logger.error("database_error", error=str(e))
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.critical("unexpected_error", error=str(e))
|
||||
raise
|
||||
|
||||
# ❌ BAD
|
||||
try:
|
||||
user = get_user(user_id)
|
||||
except: # Never use bare except!
|
||||
pass
|
||||
|
||||
try:
|
||||
user = get_user(user_id)
|
||||
except Exception: # Too broad
|
||||
pass
|
||||
```
|
||||
|
||||
### Custom Exception Hierarchy
|
||||
|
||||
```python
|
||||
class PeikarbandException(Exception):
|
||||
"""Base exception."""
|
||||
pass
|
||||
|
||||
class DomainException(PeikarbandException):
|
||||
"""Domain layer exceptions."""
|
||||
pass
|
||||
|
||||
class UserNotFoundException(DomainException):
|
||||
"""User not found."""
|
||||
pass
|
||||
```
|
||||
|
||||
## Code Organization
|
||||
|
||||
### Imports
|
||||
|
||||
```python
|
||||
# 1. Standard library
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, List
|
||||
from decimal import Decimal
|
||||
|
||||
# 2. Third-party
|
||||
import redis
|
||||
from sqlalchemy import Column, Integer
|
||||
from pydantic import BaseModel
|
||||
|
||||
# 3. Local
|
||||
from src.config.settings import settings
|
||||
from src.core.domain.entities.user import User
|
||||
```
|
||||
|
||||
### Function Length
|
||||
|
||||
**Max 50 lines per function** (توصیه: 20-30 lines)
|
||||
|
||||
```python
|
||||
# ✅ GOOD: Short and focused
|
||||
def calculate_discount(amount: Decimal, user: User) -> Decimal:
|
||||
"""Calculate user discount."""
|
||||
if user.is_premium:
|
||||
return amount * Decimal("0.10")
|
||||
return Decimal("0.00")
|
||||
|
||||
# ❌ BAD: Too long (100+ lines)
|
||||
def process_order(order_data):
|
||||
# 100+ lines of code
|
||||
pass
|
||||
```
|
||||
|
||||
### Class Length
|
||||
|
||||
**Max 300 lines per class** (توصیه: 100-200 lines)
|
||||
|
||||
اگر class بزرگ شد، به چند class کوچکتر تقسیم کنید.
|
||||
|
||||
## Comments
|
||||
|
||||
### When to Comment
|
||||
|
||||
```python
|
||||
# ✅ GOOD: Explain WHY, not WHAT
|
||||
def calculate_tax(amount: Decimal) -> Decimal:
|
||||
# Iranian tax rate is 9% as of 2024
|
||||
TAX_RATE = Decimal("0.09")
|
||||
return amount * TAX_RATE
|
||||
|
||||
# ❌ BAD: States the obvious
|
||||
def add(a, b):
|
||||
# Add two numbers
|
||||
return a + b
|
||||
```
|
||||
|
||||
### TODO Comments
|
||||
|
||||
```python
|
||||
# TODO(username): Description of what needs to be done
|
||||
# TODO(john): Implement caching for this query
|
||||
# FIXME(jane): This breaks when amount is negative
|
||||
# HACK(bob): Temporary workaround until API is fixed
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Single Responsibility
|
||||
|
||||
```python
|
||||
# ✅ GOOD
|
||||
class UserRepository:
|
||||
def save(self, user: User) -> User:
|
||||
pass
|
||||
|
||||
class EmailService:
|
||||
def send_email(self, to: str, subject: str) -> bool:
|
||||
pass
|
||||
|
||||
# ❌ BAD
|
||||
class UserManager:
|
||||
def save_user(self, user):
|
||||
pass
|
||||
|
||||
def send_email(self, user):
|
||||
pass
|
||||
|
||||
def log_activity(self, user):
|
||||
pass
|
||||
```
|
||||
|
||||
### 2. DRY (Don't Repeat Yourself)
|
||||
|
||||
```python
|
||||
# ❌ BAD
|
||||
def get_user_name(user_id):
|
||||
db = connect_db()
|
||||
user = db.query(User).filter_by(id=user_id).first()
|
||||
db.close()
|
||||
return user.name
|
||||
|
||||
def get_user_email(user_id):
|
||||
db = connect_db()
|
||||
user = db.query(User).filter_by(id=user_id).first()
|
||||
db.close()
|
||||
return user.email
|
||||
|
||||
# ✅ GOOD
|
||||
def get_user(user_id: int) -> Optional[User]:
|
||||
with get_db_context() as db:
|
||||
return db.query(User).filter_by(id=user_id).first()
|
||||
|
||||
def get_user_name(user_id: int) -> Optional[str]:
|
||||
user = get_user(user_id)
|
||||
return user.name if user else None
|
||||
```
|
||||
|
||||
### 3. Early Return
|
||||
|
||||
```python
|
||||
# ✅ GOOD: Early return
|
||||
def process_payment(amount: Decimal) -> bool:
|
||||
if amount <= 0:
|
||||
return False
|
||||
|
||||
if not user.has_sufficient_balance(amount):
|
||||
return False
|
||||
|
||||
# Process payment
|
||||
return True
|
||||
|
||||
# ❌ BAD: Nested conditions
|
||||
def process_payment(amount):
|
||||
if amount > 0:
|
||||
if user.has_sufficient_balance(amount):
|
||||
# Process payment
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
### 4. Use Constants
|
||||
|
||||
```python
|
||||
# ✅ GOOD
|
||||
MAX_LOGIN_ATTEMPTS = 3
|
||||
PASSWORD_MIN_LENGTH = 8
|
||||
SESSION_TIMEOUT_MINUTES = 30
|
||||
|
||||
if attempts >= MAX_LOGIN_ATTEMPTS:
|
||||
lock_account()
|
||||
|
||||
# ❌ BAD: Magic numbers
|
||||
if attempts >= 3: # What's 3?
|
||||
lock_account()
|
||||
```
|
||||
|
||||
### 5. Avoid Deep Nesting
|
||||
|
||||
```python
|
||||
# ✅ GOOD: Max 2-3 levels
|
||||
def process_order(order: Order) -> bool:
|
||||
if not order.is_valid():
|
||||
return False
|
||||
|
||||
if not user.can_order():
|
||||
return False
|
||||
|
||||
return save_order(order)
|
||||
|
||||
# ❌ BAD: Too nested
|
||||
def process_order(order):
|
||||
if order:
|
||||
if order.is_valid():
|
||||
if user:
|
||||
if user.can_order():
|
||||
if save_order(order):
|
||||
return True
|
||||
return False
|
||||
```
|
||||
|
||||
## Code Review Checklist
|
||||
|
||||
قبل از ارسال PR، این موارد را بررسی کنید:
|
||||
|
||||
- [ ] همه تستها pass میشوند
|
||||
- [ ] Code coverage کافی است (>80%)
|
||||
- [ ] تمام functions دارای type hints هستند
|
||||
- [ ] تمام public functions دارای docstring هستند
|
||||
- [ ] black, flake8, mypy بدون error
|
||||
- [ ] هیچ TODO/FIXME جدید بدون توضیح نیست
|
||||
- [ ] تغییرات در CHANGELOG.md ثبت شده
|
||||
- [ ] مستندات بهروز شده
|
||||
|
||||
---
|
||||
|
||||
**الزامات**: این استانداردها الزامی هستند و در code review بررسی میشوند.
|
||||
|
||||
Reference in New Issue
Block a user