Architecture Patterns

by secondsky88378361314fMIT227 starsListed Oct 8, 2026Updated Oct 8, 2026Repository updated 10 days ago

Implement proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design. Use when architecting complex backend systems or refactoring existing applications for better maintainability.

Instructions onlySoftware Development
AI-generated overview

Guides backend architecture design using Clean Architecture, Hexagonal Architecture, and Domain-Driven Design patterns.

What it does
This skill provides reference guidance on proven backend architecture patterns: Clean Architecture, Hexagonal (Ports and Adapters) Architecture, and Domain-Driven Design. It explains layers, ports, adapters, bounded contexts, entities, value objects, aggregates, and repositories, and illustrates them with Python code examples. It also lists best practices and common pitfalls such as anemic domains, framework coupling, and over-engineering.
When to use it
Use it when designing a new backend system, refactoring a monolith for maintainability, setting team architecture standards, or planning microservices decomposition. It also fits when applying domain-driven design principles or aiming for testable, loosely coupled codebases.
Requirements
No scripts or tooling are required; it is instructions and reference material only. The code examples are Python, but no runtime, packages, credentials, or network access are needed.

Architecture Patterns

Master proven backend architecture patterns including Clean Architecture, Hexagonal Architecture, and Domain-Driven Design to build maintainable, testable, and scalable systems.

When to Use This Skill

  • Designing new backend systems from scratch
  • Refactoring monolithic applications for better maintainability
  • Establishing architecture standards for your team
  • Migrating from tightly coupled to loosely coupled architectures
  • Implementing domain-driven design principles
  • Creating testable and mockable codebases
  • Planning microservices decomposition

Core Concepts

1. Clean Architecture (Uncle Bob)

Layers (dependency flows inward):

  • Entities: Core business models
  • Use Cases: Application business rules
  • Interface Adapters: Controllers, presenters, gateways
  • Frameworks & Drivers: UI, database, external services

Key Principles:

  • Dependencies point inward
  • Inner layers know nothing about outer layers
  • Business logic independent of frameworks
  • Testable without UI, database, or external services

2. Hexagonal Architecture (Ports and Adapters)

Components:

  • Domain Core: Business logic
  • Ports: Interfaces defining interactions
  • Adapters: Implementations of ports (database, REST, message queue)

Benefits:

  • Swap implementations easily (mock for testing)
  • Technology-agnostic core
  • Clear separation of concerns

3. Domain-Driven Design (DDD)

Strategic Patterns:

  • Bounded Contexts: Separate models for different domains
  • Context Mapping: How contexts relate
  • Ubiquitous Language: Shared terminology

Tactical Patterns:

  • Entities: Objects with identity
  • Value Objects: Immutable objects defined by attributes
  • Aggregates: Consistency boundaries
  • Repositories: Data access abstraction
  • Domain Events: Things that happened

Clean Architecture Pattern

Directory Structure

app/├── domain/           # Entities & business rules│   ├── entities/│   ├── value_objects/│   └── interfaces/   # Abstract interfaces├── use_cases/        # Application business rules├── adapters/         # Interface implementations│   ├── repositories/│   ├── controllers/│   └── gateways/└── infrastructure/   # Framework & external concerns

Implementation Example

python
# domain/entities/user.pyfrom dataclasses import dataclassfrom datetime import datetime
@dataclassclass User:    """Core user entity - no framework dependencies."""    id: str    email: str    name: str    created_at: datetime    is_active: bool = True
    def deactivate(self):        """Business rule: deactivating user."""        self.is_active = False
    def can_place_order(self) -> bool:        """Business rule: active users can order."""        return self.is_active
# domain/interfaces/user_repository.pyfrom abc import ABC, abstractmethodfrom typing import Optional
class IUserRepository(ABC):    """Port: defines contract, no implementation."""
    @abstractmethod    async def find_by_id(self, user_id: str) -> Optional[User]:        pass
    @abstractmethod    async def save(self, user: User) -> User:        pass
# use_cases/create_user.pyfrom dataclasses import dataclassimport uuidfrom datetime import datetime
@dataclassclass CreateUserRequest:    email: str    name: str
class CreateUserUseCase:    """Use case: orchestrates business logic."""
    def __init__(self, user_repository: IUserRepository):        self.user_repository = user_repository
    async def execute(self, request: CreateUserRequest) -> CreateUserResponse:        # Business validation        existing = await self.user_repository.find_by_email(request.email)        if existing:            return CreateUserResponse(user=None, success=False, error="Email already exists")
        # Create entity        user = User(            id=str(uuid.uuid4()),            email=request.email,            name=request.name,            created_at=datetime.now(),            is_active=True        )
        # Persist        saved_user = await self.user_repository.save(user)        return CreateUserResponse(user=saved_user, success=True)
# adapters/repositories/postgres_user_repository.pyclass PostgresUserRepository(IUserRepository):    """Adapter: PostgreSQL implementation."""
    def __init__(self, pool):        self.pool = pool
    async def find_by_id(self, user_id: str) -> Optional[User]:        async with self.pool.acquire() as conn:            row = await conn.fetchrow("SELECT * FROM users WHERE id = $1", user_id)            return self._to_entity(row) if row else None
    async def save(self, user: User) -> User:        async with self.pool.acquire() as conn:            await conn.execute(                """INSERT INTO users (id, email, name, created_at, is_active)                VALUES ($1, $2, $3, $4, $5)                ON CONFLICT (id) DO UPDATE SET email = $2, name = $3, is_active = $5""",                user.id, user.email, user.name, user.created_at, user.is_active            )            return user

Hexagonal Architecture Pattern

python
# Core domain (hexagon center)class OrderService:    """Domain service - no infrastructure dependencies."""
    def __init__(        self,        order_repository: OrderRepositoryPort,        payment_gateway: PaymentGatewayPort,        notification_service: NotificationPort    ):        self.orders = order_repository        self.payments = payment_gateway        self.notifications = notification_service
    async def place_order(self, order: Order) -> OrderResult:        # Business logic        if not order.is_valid():            return OrderResult(success=False, error="Invalid order")
        # Use ports (interfaces)        payment = await self.payments.charge(amount=order.total, customer=order.customer_id)
        if not payment.success:            return OrderResult(success=False, error="Payment failed")
        order.mark_as_paid()        saved_order = await self.orders.save(order)
        await self.notifications.send(            to=order.customer_email,            subject="Order confirmed",            body=f"Order {order.id} confirmed"        )
        return OrderResult(success=True, order=saved_order)
# Adaptersclass StripePaymentAdapter(PaymentGatewayPort):    """Primary adapter: connects to Stripe API."""    async def charge(self, amount: Money, customer: str) -> PaymentResult:        # Implementation
class MockPaymentAdapter(PaymentGatewayPort):    """Test adapter: no external dependencies."""    async def charge(self, amount: Money, customer: str) -> PaymentResult:        return PaymentResult(success=True, transaction_id="mock-123")

Domain-Driven Design Pattern

python
# Value Objects (immutable)@dataclass(frozen=True)class Email:    value: str
    def __post_init__(self):        if "@" not in self.value:            raise ValueError("Invalid email")
@dataclass(frozen=True)class Money:    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)
# Entities (with identity)class Order:    def __init__(self, id: str, customer: Customer):        self.id = id        self.customer = customer        self.items: List[OrderItem] = []        self.status = OrderStatus.PENDING        self._events: List[DomainEvent] = []
    def add_item(self, product: Product, quantity: int):        item = OrderItem(product, quantity)        self.items.append(item)        self._events.append(ItemAddedEvent(self.id, item))
    def submit(self):        if not self.items:            raise ValueError("Cannot submit empty order")        self.status = OrderStatus.SUBMITTED        self._events.append(OrderSubmittedEvent(self.id))
# Aggregates (consistency boundary)class Customer:    def __init__(self, id: str, email: Email):        self.id = id        self.email = email        self._addresses: List[Address] = []
    def add_address(self, address: Address):        if len(self._addresses) >= 5:            raise ValueError("Maximum 5 addresses allowed")        self._addresses.append(address)

Best Practices

  1. Dependency Rule: Dependencies always point inward
  2. Interface Segregation: Small, focused interfaces
  3. Business Logic in Domain: Keep frameworks out of core
  4. Test Independence: Core testable without infrastructure
  5. Bounded Contexts: Clear domain boundaries
  6. Ubiquitous Language: Consistent terminology
  7. Thin Controllers: Delegate to use cases
  8. Rich Domain Models: Behavior with data

Common Pitfalls

  • Anemic Domain: Entities with only data, no behavior
  • Framework Coupling: Business logic depends on frameworks
  • Fat Controllers: Business logic in controllers
  • Repository Leakage: Exposing ORM objects
  • Missing Abstractions: Concrete dependencies in core
  • Over-Engineering: Clean architecture for simple CRUD

Source and attribution

Source:secondsky/claude-skillsinplugins/architecture-patterns/skills/architecture-patternsat commit8837836

License: MIT

Content belongs to its original authors. SourceWeft indexes it from a public repository.

Report or request removal