ðŸŽŊ

architecture-patterns

ðŸŽŊSkill

from ovachiever/droid-tings

VibeIndex|
What it does

Implement backend architecture patterns like Clean Architecture and Domain-Driven Design to build maintainable, loosely-coupled, and scalable software systems.

ðŸ“Ķ

Part of

ovachiever/droid-tings(370 items)

architecture-patterns

Installation

git cloneClone repository
git clone https://github.com/ovachiever/droid-tings.git
📖 Extracted from docs: ovachiever/droid-tings
19Installs
-
AddedFeb 4, 2026

Skill Details

SKILL.md

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.

Overview

# 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/

│ │ ├── user.py

│ │ └── order.py

│ ├── value_objects/

│ │ ├── email.py

│ │ └── money.py

│ └── interfaces/ # Abstract interfaces

│ ├── user_repository.py

│ └── payment_gateway.py

├── use_cases/ # Application business rules

│ ├── create_user.py

│ ├── process_order.py

│ └── send_notification.py

├── adapters/ # Interface implementations

│ ├── repositories/

│ │ ├── postgres_user_repository.py

│ │ └── redis_cache_repository.py

│ ├── controllers/

│ │ └── user_controller.py

│ └── gateways/

│ ├── stripe_payment_gateway.py

│ └── sendgrid_email_gateway.py

└── infrastructure/ # Framework & external concerns

├── database.py

├── config.py

└── logging.py

```

Implementation Example

```python

# domain/entities/user.py

from dataclasses import dataclass

from datetime import datetime

from typing import Optional

@dataclass

class 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.py

from abc import ABC, abstractmethod

from typing import Optional, List

from domain.entities.user import User

class IUserRepository(ABC):

"""Port: defines contract, no implementation."""

@abstractmethod

async def find_by_id(self, user_id: str) -> Optional[User]:

pass

@abstractmethod

async def find_by_email(self, email: str) -> Optional[User]:

pass

@abstractmethod

async def save(self, user: User) -> User:

pass

@abstractmethod

async def delete(self, user_id: str) -> bool:

pass

# use_cases/create_user.py

from domain.entities.user import User

from domain.interfaces.user_repository import IUserRepository

from dataclasses import dataclass

from datetime import datetime

import uuid

@dataclass

class CreateUserRequest:

email: str

name: str

@dataclass

class CreateUserResponse:

user: User

success: bool

error: Optional[str] = None

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.py

from domain.interfaces.user_repository import IUserRepository

from domain.entities.user import User

from typing import Optional

import asyncpg

class PostgresUserRepository(IUserRepository):

"""Adapter: PostgreSQL implementation."""

def __init__(self, pool: asyncpg.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 find_by_email(self, email: str) -> Optional[User]:

async with self.pool.acquire() as conn:

row = await conn.fetchrow(

"SELECT * FROM users WHERE email = $1", email

)

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

async def delete(self, user_id: str) -> bool:

async with self.pool.acquire() as conn:

result = await conn.execute(

"DELETE FROM users WHERE id = $1", user_id

)

return result == "DELETE 1"

def _to_entity(self, row) -> User:

"""Map database row to entity."""

return User(

id=row["id"],

email=row["email"],

name=row["name"],

created_at=row["created_at"],

is_active=row["is_active"]

)

# adapters/controllers/user_controller.py

from fastapi import APIRouter, Depends, HTTPException

from use_cases.create_user import CreateUserUseCase, CreateUserRequest

from pydantic import BaseModel

router = APIRouter()

class CreateUserDTO(BaseModel):

email: str

name: str

@router.post("/users")

async def create_user(

dto: CreateUserDTO,

use_case: CreateUserUseCase = Depends(get_create_user_use_case)

):

"""Controller: handles HTTP concerns only."""

request = CreateUserRequest(email=dto.email, name=dto.name)

response = await use_case.execute(request)

if not response.success:

raise HTTPException(status_code=400, detail=response.error)

return {"user": response.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)

# Ports (interfaces)

class OrderRepositoryPort(ABC):

@abstractmethod

async def save(self, order: Order) -> Order:

pass

class PaymentGatewayPort(ABC):

@abstractmethod

async def charge(self, amount: Money, customer: str) -> PaymentResult:

pass

class NotificationPort(ABC):

@abstractmethod

async def send(self, to: str, subject: str, body: str):

pass

# Adapters (implementations)

class StripePaymentAdapter(PaymentGatewayPort):

"""Primary adapter: connects to Stripe API."""

def __init__(self, api_key: str):

self.stripe = stripe

self.stripe.api_key = api_key

async def charge(self, amount: Money, customer: str) -> PaymentResult:

try:

charge = self.stripe.Charge.create(

amount=amount.cents,

currency=amount.currency,

customer=customer

)

return PaymentResult(success=True, transaction_id=charge.id)

except stripe.error.CardError as e:

return PaymentResult(success=False, error=str(e))

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)

from dataclasses import dataclass

from typing import Optional

@dataclass(frozen=True)

class Email:

"""Value object: validated email."""

value: str

def __post_init__(self):

if "@" not in self.value:

raise ValueError("Invalid email")

@dataclass(frozen=True)

class Money:

"""Value object: amount with currency."""

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:

"""Entity: has identity, mutable state."""

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):

"""Business logic in entity."""

item = OrderItem(product, quantity)

self.items.append(item)

self._events.append(ItemAddedEvent(self.id, item))

def total(self) -> Money:

"""Calculated property."""

return sum(item.subtotal() for item in self.items)

def submit(self):

"""State transition with business rules."""

if not self.items:

raise ValueError("Cannot submit empty order")

if self.status != OrderStatus.PENDING:

raise ValueError("Order already submitted")

self.status = OrderStatus.SUBMITTED

self._events.append(OrderSubmittedEvent(self.id))

# Aggregates (consistency boundary)

class Customer:

"""Aggregate root: controls access to entities."""

def __init__(self, id: str, email: Email):

self.id = id

self.email = email

self._addresses: List[Address] = []

self._orders: List[str] = [] # Order IDs, not full objects

def add_address(self, address: Address):

"""Aggregate enforces invariants."""

if len(self._addresses) >= 5:

raise ValueError("Maximum 5 addresses allowed")

self._addresses.append(address)

@property

def primary_address(self) -> Optional[Address]:

return next((a for a in self._addresses if a.is_primary), None)

# Domain Events

@dataclass

class OrderSubmittedEvent:

order_id: str

occurred_at: datetime = field(default_factory=datetime.now)

# Repository (aggregate persistence)

class OrderRepository:

"""Repository: persist/retrieve aggregates."""

async def find_by_id(self, order_id: str) -> Optional[Order]:

"""Reconstitute aggregate from storage."""

pass

async def save(self, order: Order):

"""Persist aggregate and publish events."""

await self._persist(order)

await self._publish_events(order._events)

order._events.clear()

```

Resources

  • references/clean-architecture-guide.md: Detailed layer breakdown
  • references/hexagonal-architecture-guide.md: Ports and adapters patterns
  • references/ddd-tactical-patterns.md: Entities, value objects, aggregates
  • assets/clean-architecture-template/: Complete project structure
  • assets/ddd-examples/: Domain modeling examples

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