CtrlK
BlogDocsLog inGet started
Tessl Logo

giuseppe-trisciuoglio/developer-kit

Comprehensive developer toolkit providing reusable skills for Java/Spring Boot, TypeScript/NestJS/React/Next.js, Python, PHP, AWS CloudFormation, AI/RAG, DevOps, and more.

90

Quality

90%

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Risky

Do not use without reviewing

This version of the tile failed moderation
Moderation pipeline encountered an internal error
Overview
Quality
Evals
Security
Files

python-clean-architecture.mdplugins/developer-kit-python/skills/clean-architecture/references/

Python Clean Architecture Patterns

This reference covers Python-specific patterns for implementing Clean Architecture, Hexagonal Architecture, and Domain-Driven Design.

Python 3.11+ Features for Clean Architecture

Dataclasses with Slots (Python 3.10+)

from dataclasses import dataclass


@dataclass(frozen=True, slots=True)
class Money:
    """Value object with slots for memory efficiency."""
    amount: int  # cents
    currency: str

    def add(self, other: "Money") -> "Money":
        if self.currency != other.currency:
            raise ValueError("Currency mismatch")
        return Money(self.amount + other.amount, self.currency)

Pattern Matching for Domain Logic (Python 3.10+)

from dataclasses import dataclass
from typing import Union


@dataclass
class CreditCardPayment:
    card_number: str
    expiry: str
    cvv: str


@dataclass
class PayPalPayment:
    email: str


@dataclass
class BankTransferPayment:
    iban: str
    bic: str


PaymentMethod = Union[CreditCardPayment, PayPalPayment, BankTransferPayment]


class PaymentProcessor:
    async def process(self, amount: Money, method: PaymentMethod) -> PaymentResult:
        match method:
            case CreditCardPayment(card_number, expiry, cvv):
                return await self._process_card(amount, card_number, expiry, cvv)
            case PayPalPayment(email):
                return await self._process_paypal(amount, email)
            case BankTransferPayment(iban, bic):
                return await self._process_transfer(amount, iban, bic)
            case _:
                raise ValueError("Unknown payment method")

Self Type for Fluent Interfaces (Python 3.11+)

from typing import Self


class Order:
    def __init__(self):
        self._items: list[OrderItem] = []
        self._status = OrderStatus.DRAFT

    def add_item(self, item: OrderItem) -> Self:
        self._items.append(item)
        return self

    def apply_discount(self, percentage: float) -> Self:
        for item in self._items:
            item.apply_discount(percentage)
        return self

    def finalize(self) -> Self:
        self._status = OrderStatus.FINALIZED
        return self


# Usage: fluent interface
order = (
    Order()
    .add_item(OrderItem("Product A", 100))
    .add_item(OrderItem("Product B", 200))
    .apply_discount(0.1)
    .finalize()
)

Repository Pattern Implementations

Generic Repository Base

from abc import ABC, abstractmethod
from typing import TypeVar, Generic, List, Optional
from uuid import UUID

T = TypeVar('T')


class IRepository(ABC, Generic[T]):
    """Generic repository interface."""

    @abstractmethod
    async def find_by_id(self, entity_id: UUID) -> Optional[T]:
        pass

    @abstractmethod
    async def find_all(self) -> List[T]:
        pass

    @abstractmethod
    async def save(self, entity: T) -> T:
        pass

    @abstractmethod
    async def delete(self, entity_id: UUID) -> bool:
        pass


class IUnitOfWork(ABC):
    """Unit of Work pattern for transaction management."""

    @abstractmethod
    async def commit(self) -> None:
        pass

    @abstractmethod
    async def rollback(self) -> None:
        pass

    async def __aenter__(self):
        return self

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            await self.rollback()
        else:
            await self.commit()

SQLAlchemy 2.0 Repository

from typing import Type, TypeVar, List, Optional
from uuid import UUID

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, update, delete
from sqlalchemy.orm import DeclarativeBase

T = TypeVar('T', bound=DeclarativeBase)
E = TypeVar('E')  # Entity type


class SQLAlchemyRepository(IRepository[E], Generic[T, E]):
    """Generic SQLAlchemy repository implementation."""

    def __init__(self, session: AsyncSession, model_class: Type[T]):
        self._session = session
        self._model_class = model_class

    async def find_by_id(self, entity_id: UUID) -> Optional[E]:
        result = await self._session.execute(
            select(self._model_class).where(self._model_class.id == entity_id)
        )
        model = result.scalar_one_or_none()
        return self._to_entity(model) if model else None

    async def find_all(self) -> List[E]:
        result = await self._session.execute(select(self._model_class))
        models = result.scalars().all()
        return [self._to_entity(m) for m in models]

    async def save(self, entity: E) -> E:
        model = self._to_model(entity)
        self._session.add(model)
        await self._session.flush()
        return entity

    async def delete(self, entity_id: UUID) -> bool:
        result = await self._session.execute(
            delete(self._model_class).where(self._model_class.id == entity_id)
        )
        return result.rowcount > 0

    @abstractmethod
    def _to_entity(self, model: T) -> E:
        """Convert model to domain entity."""
        pass

    @abstractmethod
    def _to_model(self, entity: E) -> T:
        """Convert domain entity to model."""
        pass

In-Memory Repository for Testing

from typing import Dict, List, Optional
from uuid import UUID


class InMemoryRepository(IRepository[E], Generic[E]):
    """In-memory repository for testing."""

    def __init__(self):
        self._storage: Dict[UUID, E] = {}

    async def find_by_id(self, entity_id: UUID) -> Optional[E]:
        return self._storage.get(entity_id)

    async def find_all(self) -> List[E]:
        return list(self._storage.values())

    async def save(self, entity: E) -> E:
        # Assuming entity has an 'id' attribute
        self._storage[entity.id] = entity
        return entity

    async def delete(self, entity_id: UUID) -> bool:
        if entity_id in self._storage:
            del self._storage[entity_id]
            return True
        return False

    def clear(self) -> None:
        self._storage.clear()

Domain Events

Event Bus Implementation

from abc import ABC, abstractmethod
from dataclasses import dataclass
from datetime import datetime
from typing import List, Callable, Type, Dict, Any
from collections import defaultdict
import asyncio


@dataclass
class DomainEvent:
    """Base class for domain events."""
    occurred_at: datetime = field(default_factory=datetime.utcnow)


class IEventBus(ABC):
    """Event bus interface for publishing and subscribing to events."""

    @abstractmethod
    async def publish(self, event: DomainEvent) -> None:
        pass

    @abstractmethod
    def subscribe(self, event_type: Type[DomainEvent], handler: Callable) -> None:
        pass


class InMemoryEventBus(IEventBus):
    """In-memory event bus implementation."""

    def __init__(self):
        self._handlers: Dict[Type[DomainEvent], List[Callable]] = defaultdict(list)

    def subscribe(self, event_type: Type[DomainEvent], handler: Callable) -> None:
        self._handlers[event_type].append(handler)

    async def publish(self, event: DomainEvent) -> None:
        handlers = self._handlers.get(type(event), [])
        await asyncio.gather(
            *[handler(event) for handler in handlers],
            return_exceptions=True
        )


class OutboxPattern(IEventBus):
    """Outbox pattern for reliable event publishing."""

    def __init__(self, event_bus: IEventBus, outbox_repository):
        self._event_bus = event_bus
        self._outbox = outbox_repository

    async def publish(self, event: DomainEvent) -> None:
        # Store in outbox first
        await self._outbox.store(event)

    async def process_outbox(self) -> None:
        """Process pending events - call this after transaction commit."""
        events = await self._outbox.get_pending()
        for event in events:
            try:
                await self._event_bus.publish(event)
                await self._outbox.mark_as_processed(event)
            except Exception:
                await self._outbox.mark_as_failed(event)
                raise

Specification Pattern

from abc import ABC, abstractmethod
from typing import List, TypeVar

T = TypeVar('T')


class Specification(ABC, Generic[T]):
    """Specification pattern for business rules."""

    @abstractmethod
    def is_satisfied_by(self, candidate: T) -> bool:
        pass

    def __and__(self, other: "Specification[T]") -> "AndSpecification[T]":
        return AndSpecification(self, other)

    def __or__(self, other: "Specification[T]") -> "OrSpecification[T]":
        return OrSpecification(self, other)

    def __invert__(self) -> "NotSpecification[T]":
        return NotSpecification(self)


class AndSpecification(Specification[T]):
    def __init__(self, left: Specification[T], right: Specification[T]):
        self._left = left
        self._right = right

    def is_satisfied_by(self, candidate: T) -> bool:
        return self._left.is_satisfied_by(candidate) and self._right.is_satisfied_by(candidate)


class OrSpecification(Specification[T]):
    def __init__(self, left: Specification[T], right: Specification[T]):
        self._left = left
        self._right = right

    def is_satisfied_by(self, candidate: T) -> bool:
        return self._left.is_satisfied_by(candidate) or self._right.is_satisfied_by(candidate)


class NotSpecification(Specification[T]):
    def __init__(self, spec: Specification[T]):
        self._spec = spec

    def is_satisfied_by(self, candidate: T) -> bool:
        return not self._spec.is_satisfied_by(candidate)


# Example usage
class PremiumCustomerSpecification(Specification[Customer]):
    def is_satisfied_by(self, customer: Customer) -> bool:
        return customer.total_orders > 100 and customer.average_order_value > 500


class ActiveCustomerSpecification(Specification[Customer]):
    def is_satisfied_by(self, customer: Customer) -> bool:
        return customer.last_order_date > datetime.now() - timedelta(days=90)


# Combine specifications
eligible_for_vip = PremiumCustomerSpecification() & ActiveCustomerSpecification()
customers = [c for c in all_customers if eligible_for_vip.is_satisfied_by(c)]

Result Pattern for Error Handling

from dataclasses import dataclass
from typing import Generic, TypeVar, Union

T = TypeVar('T')
E = TypeVar('E')


@dataclass(frozen=True)
class Ok(Generic[T]):
    value: T


@dataclass(frozen=True)
class Err(Generic[E]):
    error: E


Result = Union[Ok[T], Err[E]]


class DomainError:
    """Base class for domain errors."""
    pass


@dataclass(frozen=True)
class ValidationError(DomainError):
    field: str
    message: str


@dataclass(frozen=True)
class NotFoundError(DomainError):
    resource: str
    identifier: str


# Usage in use cases
class CreateOrderUseCase:
    async def execute(self, request: CreateOrderRequest) -> Result[Order, DomainError]:
        customer = await self._customer_repo.find_by_id(request.customer_id)
        if not customer:
            return Err(NotFoundError("Customer", str(request.customer_id)))

        if not customer.can_place_orders():
            return Err(ValidationError("customer", "Customer cannot place orders"))

        order = Order.create(customer, request.items)
        await self._order_repo.save(order)
        return Ok(order)

Manual Dependency Injection

from typing import TypeVar, Callable, Dict, Type, Any

T = TypeVar('T')


class Container:
    """Simple manual dependency injection container."""

    def __init__(self):
        self._registrations: Dict[Type, Callable] = {}
        self._singletons: Dict[Type, Any] = {}

    def register(self, interface: Type[T], factory: Callable[[], T]) -> None:
        """Register a factory for an interface."""
        self._registrations[interface] = factory

    def register_instance(self, interface: Type[T], instance: T) -> None:
        """Register a singleton instance."""
        self._singletons[interface] = instance

    def resolve(self, interface: Type[T]) -> T:
        """Resolve an interface to its implementation."""
        if interface in self._singletons:
            return self._singletons[interface]

        if interface not in self._registrations:
            raise KeyError(f"No registration for {interface}")

        return self._registrations[interface]()


# Usage
container = Container()

# Register repositories
container.register(
    IUserRepository,
    lambda: SQLAlchemyUserRepository(get_db_session())
)

# Register use cases
container.register(
    CreateUserUseCase,
    lambda: CreateUserUseCase(container.resolve(IUserRepository))
)

# Resolve
use_case = container.resolve(CreateUserUseCase)

Async Context Managers for Resources

from contextlib import asynccontextmanager
from typing import AsyncGenerator


@asynccontextmanager
async def unit_of_work(session: AsyncSession) -> AsyncGenerator[UnitOfWork, None]:
    """Context manager for transaction handling."""
    uow = UnitOfWork(session)
    try:
        yield uow
        await uow.commit()
    except Exception:
        await uow.rollback()
        raise


# Usage
async def create_user_handler(request: CreateUserRequest):
    async with unit_of_work(session) as uow:
        use_case = CreateUserUseCase(uow.user_repository)
        result = await use_case.execute(request)
        # Automatically commits or rolls back

Type Hints Best Practices

from typing import NewType, Protocol
from uuid import UUID

# NewType for type safety
UserId = NewType('UserId', UUID)
OrderId = NewType('OrderId', UUID)


def get_user(user_id: UserId) -> User: ...
def get_order(order_id: OrderId) -> Order: ...

# This will be caught by type checker:
# get_user(order_id)  # Error: Expected UserId, got OrderId


# Protocol for structural typing
class Logger(Protocol):
    def debug(self, msg: str) -> None: ...
    def info(self, msg: str) -> None: ...
    def error(self, msg: str) -> None: ...


class UseCase:
    def __init__(self, logger: Logger) -> None:
        self._logger = logger


# Any object with debug/info/error methods works
class ConsoleLogger:
    def debug(self, msg: str) -> None: print(f"DEBUG: {msg}")
    def info(self, msg: str) -> None: print(f"INFO: {msg}")
    def error(self, msg: str) -> None: print(f"ERROR: {msg}")


use_case = UseCase(ConsoleLogger())  # Works!

plugins

CHANGELOG.md

context7.json

CONTRIBUTING.md

README_CN.md

README_ES.md

README_IT.md

README.md

tessl.json

tile.json