साफ़ आर्किटेक्चर के लिए पाइथन डिज़ाइन पैटर्न
SOLID डिज़ाइन पैटर्न के साथ बनाएं Python एप्लिकेशन जो आसानी से बनाए रखे जा सकें
साफ़ आर्किटेक्चर ने विकसकों को स्केलेबल, मेन्टेनएबल एप्लिकेशन्स बनाने के तरीके को क्रांतिकारी बनाया है, जिसमें अलग-अलग चिंताओं का प्रबंधन और डिपेंडेंसी मैनेजमेंट पर जोर दिया गया है।
पाइथन में, ये सिद्धांत भाषा की डायनामिक प्रकृति के साथ मिलकर फ्लेक्सिबल, टेस्टेबल सिस्टम्स बनाते हैं जो बिजनेस रिक्वायरमेंट्स के साथ विकसित होते हैं बिना टेक्निकल डेब्ट में बदलने के।

साफ़ आर्किटेक्चर को पाइथन में समझना
साफ़ आर्किटेक्चर, जिसे रॉबर्ट सी. मार्टिन (अंकल बॉब) ने पेश किया था, सॉफ्टवेयर को केंद्रीय परतों में व्यवस्थित करता है जहां डिपेंडेंसीज़ कोर बिजनेस लॉजिक की ओर इशारा करते हैं। यह आर्किटेक्चरल पैटर्न सुनिश्चित करता है कि आपकी एप्लिकेशन की महत्वपूर्ण बिजनेस रूल्स फ्रेमवर्क्स, डेटाबेस, और बाहरी सर्विसेज से स्वतंत्र रहें।
कोर दर्शन
मूलभूत सिद्धांत सरल है लेकिन शक्तिशाली: बिजनेस लॉजिक को इन्फ्रास्ट्रक्चर पर निर्भर नहीं होना चाहिए। आपके डोमेन एंटिटीज़, यूज़ केसेज, और बिजनेस रूल्स यह काम कर सकते हैं कि आप PostgreSQL या MongoDB, FastAPI या Flask, AWS या Azure का उपयोग कर रहे हैं या नहीं।
पाइथन में, यह दर्शन “डक टाइपिंग” और प्रोटोकॉल-ओरिएंटेड प्रोग्रामिंग के साथ पूरी तरह से मेल खाता है, जो स्टैटिकली टाइप्ड भाषाओं में आवश्यक अनुष्ठान के बिना साफ़ अलगाव प्रदान करता है।
साफ़ आर्किटेक्चर की चार परतें
एंटिटीज़ परत (डोमेन): बाहरी डिपेंडेंसीज़ के बिना प्यूर बिजनेस ऑब्जेक्ट्स के साथ एंटरप्राइज़-वाइड बिजनेस रूल्स। ये POJOs (प्लेन ओल्ड पाइथन ऑब्जेक्ट्स) होते हैं।
यूज़ केसेज परत (एप्लिकेशन): एंटिटीज़ और बाहरी सर्विसेज के बीच डेटा के प्रवाह को ऑर्केस्ट्रेट करने वाले एप्लिकेशन-स्पेसिफिक बिजनेस रूल्स।
इंटरफेस एडैप्टर्स परत: यूज़ केसेज और एंटिटीज़ के लिए सबसे सुविधाजनक फॉर्मेट और बाहरी एजेंसियों द्वारा आवश्यक फॉर्मेट के बीच डेटा को कन्वर्ट करता है।
फ्रेमवर्क्स और ड्राइवर्स परत: डेटाबेस, वेब फ्रेमवर्क्स, और बाहरी एपीआई जैसे सभी बाहरी विवरण।
SOLID सिद्धांत पाइथन में
SOLID सिद्धांत साफ़ आर्किटेक्चर के आधार हैं। आइए देखें कि हर सिद्धांत पाइथन में कैसे प्रकट होता है। डिज़ाइन पैटर्न्स के बारे में एक व्यापक अवलोकन के लिए, पाइथन डिज़ाइन पैटर्न्स गाइड देखें।
सिंगल रिस्पॉन्सिबिलिटी सिद्धांत (SRP)
हर क्लास को बदलने का एक ही कारण होना चाहिए:
# बुरा: कई जिम्मेदारियाँ
class UserManager:
def create_user(self, user_data):
# यूज़र बनाना
pass
def send_welcome_email(self, user):
# ईमेल भेजना
pass
def log_creation(self, user):
# फाइल में लॉग करना
pass
# अच्छा: अलग-अलग जिम्मेदारियाँ
class UserService:
def __init__(self, repository, email_service, logger):
self.repository = repository
self.email_service = email_service
self.logger = logger
def create_user(self, user_data):
user = User(**user_data)
self.repository.save(user)
self.email_service.send_welcome(user)
self.logger.info(f"यूज़र बनाया गया: {user.id}")
return user
ओपन/क्लोज्ड सिद्धांत (OCP)
सॉफ्टवेयर एंटिटीज़ को एक्सटेंशन के लिए ओपन रखना चाहिए लेकिन मॉडिफिकेशन के लिए क्लोज्ड:
from abc import ABC, abstractmethod
from typing import Protocol
# प्रोटोकॉल का उपयोग (पाइथन 3.8+)
class PaymentProcessor(Protocol):
def process_payment(self, amount: float) -> bool:
...
class CreditCardProcessor:
def process_payment(self, amount: float) -> bool:
# क्रेडिट कार्ड लॉजिक
return True
class PayPalProcessor:
def process_payment(self, amount: float) -> bool:
# PayPal लॉजिक
return True
# मौजूदा कोड को मॉडिफाई किए बिना आसानी से एक्सटेंसिबल
class CryptoProcessor:
def process_payment(self, amount: float) -> bool:
# क्रिप्टोक्यूरेंसी लॉजिक
return True
लिस्कोव सब्सटिट्यूशन सिद्धांत (LSP)
ऑब्जेक्ट्स को अपने सबटाइप्स के साथ बदलने से प्रोग्राम टूटना नहीं चाहिए:
from abc import ABC, abstractmethod
class DataStore(ABC):
@abstractmethod
def save(self, key: str, value: str) -> None:
pass
@abstractmethod
def get(self, key: str) -> str:
pass
class PostgreSQLStore(DataStore):
def save(self, key: str, value: str) -> None:
# PostgreSQL इम्प्लीमेंटेशन
pass
def get(self, key: str) -> str:
# PostgreSQL इम्प्लीमेंटेशन
return ""
class RedisStore(DataStore):
def save(self, key: str, value: str) -> None:
# Redis इम्प्लीमेंटेशन
pass
def get(self, key: str) -> str:
# Redis इम्प्लीमेंटेशन
return ""
# दोनों को इंटरचेंजेबल रूप से उपयोग किया जा सकता है
def process_data(store: DataStore, key: str, value: str):
store.save(key, value)
return store.get(key)
इंटरफेस सेग्रेगेशन सिद्धांत (ISP)
क्लाइंट्स को उन इंटरफेस पर निर्भर नहीं होना चाहिए जिन्हें वे उपयोग नहीं करते:
# बुरा: फैट इंटरफेस
class Worker(ABC):
@abstractmethod
def work(self): pass
@abstractmethod
def eat(self): pass
@abstractmethod
def sleep(self): pass
# अच्छा: सेग्रेगेटेड इंटरफेस
class Workable(Protocol):
def work(self) -> None: ...
class Eatable(Protocol):
def eat(self) -> None: ...
class Human:
def work(self) -> None:
print("काम कर रहा हूँ")
def eat(self) -> None:
print("खा रहा हूँ")
class Robot:
def work(self) -> None:
print("काम कर रहा हूँ")
# ईट मेथड की आवश्यकता नहीं है
डिपेंडेंसी इनवर्शन सिद्धांत (DIP)
हाई-लेवल मॉड्यूल्स को लो-लेवल मॉड्यूल्स पर निर्भर नहीं होना चाहिए। दोनों को अभिसरण पर निर्भर करना चाहिए:
from typing import Protocol
# अभिसरण
class EmailSender(Protocol):
def send(self, to: str, subject: str, body: str) -> None:
...
# लो-लेवल मॉड्यूल
class SMTPEmailSender:
def send(self, to: str, subject: str, body: str) -> None:
# SMTP इम्प्लीमेंटेशन
pass
# हाई-लेवल मॉड्यूल अभिसरण पर निर्भर है
class UserRegistrationService:
def __init__(self, email_sender: EmailSender):
self.email_sender = email_sender
def register(self, email: str, name: str):
# रजिस्ट्रेशन लॉजिक
self.email_sender.send(
to=email,
subject="स्वागत है!",
body=f"नमस्ते {name}"
)
रिपॉजिटरी पैटर्न: डेटा एक्सेस को एब्स्ट्रैक्ट करना
रिपॉजिटरी पैटर्न डोमेन ऑब्जेक्ट्स तक पहुंचने के लिए एक कलेक्शन-जैसा इंटरफेस प्रदान करता है, डेटा स्टोरेज के विवरणों को छिपाता है।
बेसिक रिपॉजिटरी इम्प्लीमेंटेशन
from abc import ABC, abstractmethod
from typing import List, Optional
from dataclasses import dataclass
from uuid import UUID, uuid4
@dataclass
class User:
id: UUID
email: str
name: str
is_active: bool = True
class UserRepository(ABC):
@abstractmethod
def save(self, user: User) -> User:
pass
@abstractmethod
def get_by_id(self, user_id: UUID) -> Optional[User]:
pass
@abstractmethod
def get_by_email(self, email: str) -> Optional[User]:
pass
@abstractmethod
def list_all(self) -> List[User]:
pass
@abstractmethod
def delete(self, user_id: UUID) -> bool:
pass
SQLAlchemy इम्प्लीमेंटेशन
from sqlalchemy import create_engine, Column, String, Boolean
from sqlalchemy.dialects.postgresql import UUID as PGUUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, Session
Base = declarative_base()
class UserModel(Base):
__tablename__ = 'users'
id = Column(PGUUID(as_uuid=True), primary_key=True)
email = Column(String, unique=True, nullable=False)
name = Column(String, nullable=False)
is_active = Column(Boolean, default=True)
class SQLAlchemyUserRepository(UserRepository):
def __init__(self, session: Session):
self.session = session
def save(self, user: User) -> User:
user_model = UserModel(
id=user.id,
email=user.email,
name=user.name,
is_active=user.is_active
)
self.session.add(user_model)
self.session.commit()
return user
def get_by_id(self, user_id: UUID) -> Optional[User]:
user_model = self.session.query(UserModel).filter(
UserModel.id == user_id
).first()
if not user_model:
return None
return User(
id=user_model.id,
email=user_model.email,
name=user_model.name,
is_active=user_model.is_active
)
def get_by_email(self, email: str) -> Optional[User]:
user_model = self.session.query(UserModel).filter(
UserModel.email == email
).first()
if not user_model:
return None
return User(
id=user_model.id,
email=user_model.email,
name=user_model.name,
is_active=user_model.is_active
)
def list_all(self) -> List[User]:
users = self.session.query(UserModel).all()
return [
User(
id=u.id,
email=u.email,
name=u.name,
is_active=u.is_active
)
for u in users
]
def delete(self, user_id: UUID) -> bool:
result = self.session.query(UserModel).filter(
UserModel.id == user_id
).delete()
self.session.commit()
return result > 0
टेस्टिंग के लिए इन-मेमोरी रिपॉजिटरी
class InMemoryUserRepository(UserRepository):
def __init__(self):
self.users: dict[UUID, User] = {}
def save(self, user: User) -> User:
self.users[user.id] = user
return user
def get_by_id(self, user_id: UUID) -> Optional[User]:
return self.users.get(user_id)
def get_by_email(self, email: str) -> Optional[User]:
for user in self.users.values():
if user.email == email:
return user
return None
def list_all(self) -> List[User]:
return list(self.users.values())
def delete(self, user_id: UUID) -> bool:
if user_id in self.users:
del self.users[user_id]
return True
return False
सेवा परत: व्यवसाय तर्क का संचालन
सेवा परत उपयोग मामलों को लागू करती है और रिपॉजिटरी, बाहरी सेवाओं, और डोमेन तर्क के बीच प्रवाह का संचालन करती है।
from typing import Optional
from uuid import uuid4
class UserAlreadyExistsError(Exception):
pass
class UserNotFoundError(Exception):
pass
class UserService:
def __init__(
self,
user_repository: UserRepository,
email_service: EmailSender,
event_publisher: 'EventPublisher'
):
self.user_repository = user_repository
self.email_service = email_service
self.event_publisher = event_publisher
def register_user(self, email: str, name: str) -> User:
# Check if user exists
existing_user = self.user_repository.get_by_email(email)
if existing_user:
raise UserAlreadyExistsError(f"User with email {email} already exists")
# Create new user
user = User(
id=uuid4(),
email=email,
name=name,
is_active=True
)
# Save to repository
user = self.user_repository.save(user)
# Send welcome email
self.email_service.send(
to=user.email,
subject="Welcome!",
body=f"Hello {user.name}, welcome to our platform!"
)
# Publish event
self.event_publisher.publish('user.registered', {
'user_id': str(user.id),
'email': user.email
})
return user
def deactivate_user(self, user_id: UUID) -> User:
user = self.user_repository.get_by_id(user_id)
if not user:
raise UserNotFoundError(f"User {user_id} not found")
user.is_active = False
user = self.user_repository.save(user)
self.event_publisher.publish('user.deactivated', {
'user_id': str(user.id)
})
return user
Python में निर्भरता इंजेक्शन
Python की गतिशील प्रकृति निर्भरता इंजेक्शन को बिना भारी फ्रेमवर्क की आवश्यकता के सरल बनाती है।
कंस्ट्रक्टर इंजेक्शन
class OrderService:
def __init__(
self,
order_repository: 'OrderRepository',
payment_processor: PaymentProcessor,
notification_service: 'NotificationService'
):
self.order_repository = order_repository
self.payment_processor = payment_processor
self.notification_service = notification_service
def place_order(self, order_data: dict):
# Use injected dependencies
pass
सरल निर्भरता कंटेनर
from typing import Dict, Type, Callable, Any
class Container:
def __init__(self):
self._services: Dict[Type, Callable] = {}
self._singletons: Dict[Type, Any] = {}
def register(self, interface: Type, factory: Callable):
self._services[interface] = factory
def register_singleton(self, interface: Type, instance: Any):
self._singletons[interface] = instance
def resolve(self, interface: Type):
if interface in self._singletons:
return self._singletons[interface]
factory = self._services.get(interface)
if factory:
return factory(self)
raise ValueError(f"No registration found for {interface}")
# Usage
def create_container() -> Container:
container = Container()
# Register services
container.register_singleton(
Session,
sessionmaker(bind=create_engine('postgresql://...'))()
)
container.register(
UserRepository,
lambda c: SQLAlchemyUserRepository(c.resolve(Session))
)
container.register(
EmailSender,
lambda c: SMTPEmailSender()
)
container.register(
UserService,
lambda c: UserService(
c.resolve(UserRepository),
c.resolve(EmailSender),
c.resolve(EventPublisher)
)
)
return container
हेक्सागोनल आर्किटेक्चर (पोर्ट्स और एडैप्टर्स)
हेक्सागोनल आर्किटेक्चर व्यवसाय तर्क को केंद्र में रखती है और एडैप्टर्स बाहरी संचार का प्रबंधन करती हैं।
पोर्ट्स (इंटरफेस) को परिभाषित करना
# Input Port (Primary)
class CreateUserUseCase(Protocol):
def execute(self, request: 'CreateUserRequest') -> 'CreateUserResponse':
...
# Output Port (Secondary)
class UserPersistencePort(Protocol):
def save(self, user: User) -> User:
...
def find_by_email(self, email: str) -> Optional[User]:
...
एडैप्टर्स को लागू करना
from pydantic import BaseModel, EmailStr
# Input Adapter (REST API)
from fastapi import FastAPI, Depends, HTTPException
class CreateUserRequest(BaseModel):
email: EmailStr
name: str
class CreateUserResponse(BaseModel):
id: str
email: str
name: str
app = FastAPI()
@app.post("/users", response_model=CreateUserResponse)
def create_user(
request: CreateUserRequest,
user_service: UserService = Depends(get_user_service)
):
try:
user = user_service.register_user(
email=request.email,
name=request.name
)
return CreateUserResponse(
id=str(user.id),
email=user.email,
name=user.name
)
except UserAlreadyExistsError as e:
raise HTTPException(status_code=400, detail=str(e))
# Output Adapter (Database)
# Already implemented as SQLAlchemyUserRepository
डोमेन-ड्राइवन डिजाइन पैटर्न्स
वैल्यू ऑब्जेक्ट्स
अपरिवर्तनीय ऑब्जेक्ट्स जो अपने गुणों द्वारा परिभाषित होते हैं:
from dataclasses import dataclass
from typing import Pattern
import re
@dataclass(frozen=True)
class Email:
value: str
EMAIL_PATTERN: Pattern = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
def __post_init__(self):
if not self.EMAIL_PATTERN.match(self.value):
raise ValueError(f"Invalid email: {self.value}")
def __str__(self):
return self.value
@dataclass(frozen=True)
class Money:
amount: float
currency: str
def __post_init__(self):
if self.amount < 0:
raise ValueError("Amount cannot be negative")
if self.currency not in ['USD', 'EUR', 'GBP']:
raise ValueError(f"Unsupported currency: {self.currency}")
def add(self, other: 'Money') -> 'Money':
if self.currency != other.currency:
raise ValueError("Cannot add different currencies")
return Money(self.amount + other.amount, self.currency)
एग्रीगेट्स
डोमेन ऑब्जेक्ट्स का एक समूह जो एक एकल इकाई के रूप में संभाला जाता है:
from dataclasses import dataclass, field
from typing import List
from datetime import datetime
@dataclass
class OrderItem:
product_id: UUID
quantity: int
price: Money
def total(self) -> Money:
return Money(
self.price.amount * self.quantity,
self.price.currency
)
@dataclass
class Order:
id: UUID
customer_id: UUID
items: List[OrderItem] = field(default_factory=list)
status: str = "pending"
created_at: datetime = field(default_factory=datetime.now)
def add_item(self, product_id: UUID, quantity: int, price: Money):
item = OrderItem(product_id, quantity, price)
self.items.append(item)
def remove_item(self, product_id: UUID):
self.items = [
item for item in self.items
if item.product_id != product_id
]
def total(self) -> Money:
if not self.items:
return Money(0, "USD")
return sum(
(item.total() for item in self.items),
Money(0, self.items[0].price.currency)
)
def confirm(self):
if not self.items:
raise ValueError("Cannot confirm empty order")
if self.status != "pending":
raise ValueError("Order already processed")
self.status = "confirmed"
डोमेन इवेंट्स
डोमेन इवेंट्स घटकों के बीच ढीले कनेक्शन को सक्षम बनाते हैं और इवेंट-ड्राइवन आर्किटेक्चर का समर्थन करते हैं। उत्पादन-स्तर के इवेंट-ड्राइवन सिस्टम के लिए, AWS Kinesis जैसे सेवाओं के साथ इवेंट स्ट्रीमिंग को लागू करने का विचार करें—Building Event-Driven Microservices with AWS Kinesis के लिए एक विस्तृत गाइड देखें।
from dataclasses import dataclass
from datetime import datetime
from typing import List, Callable
@dataclass
class DomainEvent:
occurred_at: datetime = field(default_factory=datetime.now)
@dataclass
class OrderConfirmed(DomainEvent):
order_id: UUID
customer_id: UUID
total: Money
class EventPublisher:
def __init__(self):
self._handlers: Dict[Type, List[Callable]] = {}
def subscribe(self, event_type: Type, handler: Callable):
if event_type not in self._handlers:
self._handlers[event_type] = []
self._handlers[event_type].append(handler)
def publish(self, event: DomainEvent):
event_type = type(event)
handlers = self._handlers.get(event_type, [])
for handler in handlers:
handler(event)
आधुनिक पाइथन फीचर्स क्लीन आर्किटेक्चर के लिए
पाइथन के आधुनिक फीचर्स क्लीन आर्किटेक्चर को लागू करने को अधिक सुंदर और टाइप-सेफ बनाते हैं। अगर आपको पाइथन सिंटैक्स और फीचर्स के लिए एक तेज़ संदर्भ चाहिए, तो पाइथन चीटशीट देखें।
टाइप हिंट्स और प्रोटोकॉल्स
from typing import Protocol, runtime_checkable
@runtime_checkable
class Serializable(Protocol):
def to_dict(self) -> dict:
...
@classmethod
def from_dict(cls, data: dict) -> 'Serializable':
...
def serialize(obj: Serializable) -> dict:
return obj.to_dict()
पाइडैन्टिक फॉर वैलिडेशन
from pydantic import BaseModel, Field, validator
from typing import Optional
class CreateUserDTO(BaseModel):
email: EmailStr
name: str = Field(..., min_length=2, max_length=100)
age: Optional[int] = Field(None, ge=0, le=150)
@validator('name')
def name_must_not_contain_numbers(cls, v):
if any(char.isdigit() for char in v):
raise ValueError('Name cannot contain numbers')
return v
class Config:
frozen = True # Make immutable
एसिंक्रोनस/एवेट फॉर आई/ओ ऑपरेशन्स
पाइथन का एसिंक्रोनस/एवेट सिंटैक्स क्लीन आर्किटेक्चर में आई/ओ-बाउंड ऑपरेशन्स के लिए विशेष रूप से शक्तिशाली है, जो डेटाबेस और बाहरी सेवाओं के साथ नॉन-ब्लॉकिंग इंटरैक्शन्स की अनुमति देता है। जब आप पाइथन एप्लिकेशन्स को सर्वरलेस प्लेटफॉर्म्स पर डिप्लॉय करते हैं, तो प्रदर्शन विशेषताओं को समझना महत्वपूर्ण हो जाता है—AWS लैम्ब्डा प्रदर्शन: जावास्क्रिप्ट vs पाइथन vs गोलैंग पाइथन सर्वरलेस फंक्शन्स को ऑप्टिमाइज़ करने के लिए अंतर्दृष्टि प्रदान करता है।
from typing import List
import asyncio
class AsyncUserRepository(ABC):
@abstractmethod
async def save(self, user: User) -> User:
pass
@abstractmethod
async def get_by_id(self, user_id: UUID) -> Optional[User]:
pass
class AsyncUserService:
def __init__(self, repository: AsyncUserRepository):
self.repository = repository
async def register_user(self, email: str, name: str) -> User:
user = User(id=uuid4(), email=email, name=name)
return await self.repository.save(user)
async def get_users_batch(self, user_ids: List[UUID]) -> List[User]:
tasks = [self.repository.get_by_id(uid) for uid in user_ids]
results = await asyncio.gather(*tasks)
return [u for u in results if u is not None]
प्रोजेक्ट स्ट्रक्चर बेस्ट प्रैक्टिसेस
उचित प्रोजेक्ट संगठन क्लीन आर्किटेक्चर बनाए रखने के लिए आवश्यक है। अपने प्रोजेक्ट स्ट्रक्चर सेटअप करने से पहले, सुनिश्चित करें कि आप डिपेंडेंसी आइसोलेशन के लिए पाइथन वर्चुअल एन्वायर्नमेंट्स का उपयोग कर रहे हैं। venv चीटशीट वर्चुअल एन्वायर्नमेंट्स को प्रबंधित करने के बारे में आपको जानने की आवश्यकता है। आधुनिक पाइथन प्रोजेक्ट्स के लिए, uv - नया पाइथन पैकेज, प्रोजेक्ट, और एन्वायर्नमेंट मैनेजर पर विचार करें, जो तेज़ पैकेज प्रबंधन और प्रोजेक्ट सेटअप प्रदान करता है।
my_application/
├── domain/ # एंटरप्राइज बिजनेस रूल्स
│ ├── __init__.py
│ ├── entities/
│ │ ├── __init__.py
│ │ ├── user.py
│ │ └── order.py
│ ├── value_objects/
│ │ ├── __init__.py
│ │ ├── email.py
│ │ └── money.py
│ ├── events/
│ │ ├── __init__.py
│ │ └── user_events.py
│ └── exceptions.py
│
├── application/ # एप्लिकेशन बिजनेस रूल्स
│ ├── __init__.py
│ ├── use_cases/
│ │ ├── __init__.py
│ │ ├── create_user.py
│ │ └── place_order.py
│ ├── services/
│ │ ├── __init__.py
│ │ └── user_service.py
│ └── ports/
│ ├── __init__.py
│ ├── repositories.py
│ └── external_services.py
│
├── infrastructure/ # बाहरी इंटरफेस
│ ├── __init__.py
│ ├── persistence/
│ │ ├── __init__.py
│ │ ├── sqlalchemy/
│ │ │ ├── models.py
│ │ │ └── repositories.py
│ │ └── mongodb/
│ │ └── repositories.py
│ ├── messaging/
│ │ ├── __init__.py
│ │ └── rabbitmq_publisher.py
│ ├── external_services/
│ │ ├── __init__.py
│ │ └── email_service.py
│ └── config.py
│
├── presentation/ # UI/API लेयर
│ ├── __init__.py
│ ├── api/
│ │ ├── __init__.py
│ │ ├── dependencies.py
│ │ ├── routes/
│ │ │ ├── __init__.py
│ │ │ ├── users.py
│ │ │ └── orders.py
│ │ └── schemas/
│ │ ├── __init__.py
│ │ └── user_schemas.py
│ └── cli/
│ └── commands.py
│
├── tests/
│ ├── unit/
│ ├── integration/
│ └── e2e/
│
├── main.py # एप्लिकेशन एंट्री पॉइंट
├── container.py # डिपेंडेंसी इंजेक्शन सेटअप
├── pyproject.toml
└── README.md
क्लीन आर्किटेक्चर का परीक्षण
यूनिट टेस्टिंग डोमेन लॉजिक
import pytest
from uuid import uuid4
def test_user_creation():
user = User(
id=uuid4(),
email="test@example.com",
name="Test User"
)
assert user.email == "test@example.com"
assert user.is_active is True
def test_order_total_calculation():
order = Order(id=uuid4(), customer_id=uuid4())
order.add_item(
uuid4(),
quantity=2,
price=Money(10.0, "USD")
)
order.add_item(
uuid4(),
quantity=1,
price=Money(5.0, "USD")
)
assert order.total().amount == 25.0
रिपॉजिटरी के साथ इंटीग्रेशन टेस्टिंग
@pytest.fixture
def in_memory_repository():
return InMemoryUserRepository()
def test_user_repository_save_and_retrieve(in_memory_repository):
user = User(
id=uuid4(),
email="test@example.com",
name="Test User"
)
saved_user = in_memory_repository.save(user)
retrieved_user = in_memory_repository.get_by_id(user.id)
assert retrieved_user is not None
assert retrieved_user.email == user.email
सर्विस लेयर का परीक्षण
from unittest.mock import Mock
def test_user_registration():
# Arrange
mock_repository = Mock(spec=UserRepository)
mock_repository.get_by_email.return_value = None
mock_repository.save.return_value = User(
id=uuid4(),
email="test@example.com",
name="Test"
)
mock_email = Mock(spec=EmailSender)
mock_events = Mock(spec=EventPublisher)
service = UserService(mock_repository, mock_email, mock_events)
# Act
user = service.register_user("test@example.com", "Test")
# Assert
assert user.email == "test@example.com"
mock_repository.save.assert_called_once()
mock_email.send.assert_called_once()
mock_events.publish.assert_called_once()
सामान्य पिटफॉल्स और उन्हें कैसे टालें
ओवर-इंजीनियरिंग
साधारण CRUD एप्लिकेशन्स के लिए क्लीन आर्किटेक्चर लागू न करें। सरल से शुरू करें और जटिलता बढ़ने पर रीफैक्टर करें।
लीकी अब्स्ट्रैक्शन्स
सुनिश्चित करें कि डोमेन एंटिटीज में डेटाबेस एनोटेशन्स या फ्रेमवर्क-स्पेसिफिक कोड न हो:
# बुरा
from sqlalchemy import Column
@dataclass
class User:
id: Column(Integer, primary_key=True) # फ्रेमवर्क डोमेन में लीक हो रहा है
# अच्छा
@dataclass
class User:
id: UUID # शुद्ध डोमेन ऑब्जेक्ट
सर्कुलर डिपेंडेंसीज
डिपेंडेंसी इंजेक्शन और इंटरफेस का उपयोग करके लेयर्स के बीच सर्कुलर डिपेंडेंसीज तोड़ें।
कॉन्टेक्स्ट को नज़रअंदाज़ करना
क्लीन आर्किटेक्चर एक-आकार-सबके-लिए नहीं है। प्रोजेक्ट आकार और टीम विशेषज्ञता के आधार पर लेयर स्ट्रिक्टनेस को समायोजित करें।
उपयोगी लिंक्स
- Clean Architecture by Robert C. Martin
- Python Type Hints Documentation
- Pydantic Documentation
- FastAPI Official Docs
- SQLAlchemy ORM Documentation
- Dependency Injector Library
- Domain-Driven Design Reference
- Architecture Patterns with Python
- Martin Fowler’s Blog on Architecture
- Python Design Patterns Guide
- पाइथन चीटशीट
- venv चीटशीट
- uv - नया पाइथन पैकेज, प्रोजेक्ट, और एन्वायर्नमेंट मैनेजर
- AWS लैम्ब्डा प्रदर्शन: जावास्क्रिप्ट vs पाइथन vs गोलैंग
- AWS Kinesis के साथ इवेंट-ड्रिवन माइक्रोसर्विसेस बनाना