Skip to content
architecture-patterns logo

architecture-patterns

Apply Clean Architecture, Hexagonal (ports and adapters), onion architecture, layered architecture, and DDD patterns to backend systems. Define bounded contexts, create port/adapter interfaces, organize dependency layers, separate domain from infrastructure, enforce dependency inversion, and stru...

SKILL.md

Full skill instructions

Architecture Patterns

Workflow

Follow these steps sequentially when applying architecture patterns to a codebase:

1. Identify domain boundaries and define bounded contexts

  • Map the business domain into distinct bounded contexts with clear responsibilities
  • Define the ubiquitous language for each context
  • Determine context relationships (shared kernel, customer-supplier, anti-corruption layer)

2. Define core entities and value objects

  • Model entities (objects with identity and lifecycle) in the domain layer
  • Extract value objects (immutable, identity-less) for concepts like Money, Email, Address
  • Keep all business rules inside entities and value objects -- no logic in services or controllers

3. Create repository interfaces (ports)

  • Define abstract interfaces in the domain layer for all external dependencies
  • Ports include: data persistence, external APIs, messaging, notifications
  • Domain code depends only on these interfaces, never on concrete implementations

4. Implement adapters

  • Write concrete implementations of each port (Postgres repository, Stripe gateway, SQS publisher)
  • Adapters live in an outer layer and import the domain -- never the reverse
  • Create test doubles (in-memory repositories, mock gateways) implementing the same ports

5. Wire use cases to orchestrate business logic

  • Each use case class receives ports via constructor injection
  • Use cases coordinate domain objects and ports to fulfill a single application operation
  • Return result objects, not raw domain entities, to decouple callers from domain internals

6. Validate architecture constraints

  • Dependency rule: all dependencies point inward (infrastructure -> application -> domain)
  • Domain purity: domain layer has zero framework imports
  • Interface segregation: ports are small and focused on one capability
  • Thin controllers: HTTP/​CLI handlers only translate input/​output and delegate to use cases

Reference Implementation

This trimmed example shows the key relationships: entity -> port -> use case -> adapter.

# domain/​entities/​user.py — Core entity, no framework dependencies
@dataclass
class User:
    id: str
    email: str
    name: str
    is_active: bool = True

    def deactivate(self):
        self.is_active = False

# domain/​interfaces/​user_repository.py — Port (abstract interface in domain)
class IUserRepository(ABC):
    @abstractmethod
    async def find_by_email(self, email: str) -> Optional[User]: ...
    @abstractmethod
    async def save(self, user: User) -> User: ...

# use_cases/​create_user.py — Orchestrates domain + ports
class CreateUserUseCase:
    def __init__(self, user_repo: IUserRepository):
        self.user_repo = user_repo

    async def execute(self, email: str, name: str) -> User:
        if await self.user_repo.find_by_email(email):
            raise ValueError("Email already exists")
        user = User(id=str(uuid4()), email=email, name=name)
        return await self.user_repo.save(user)

# adapters/​postgres_user_repository.py — Adapter (implements port)
class PostgresUserRepository(IUserRepository):
    def __init__(self, pool: asyncpg.Pool):
        self.pool = pool

    async def find_by_email(self, email: str) -> Optional[User]:
        row = await self.pool.fetchrow("SELECT * FROM users WHERE email=$1", email)
        return User(**row) if row else None

    async def save(self, user: User) -> User:
        await self.pool.execute(
            "INSERT INTO users (id,email,name,is_active) VALUES ($1,$2,$3,$4) "
            "ON CONFLICT (id) DO UPDATE SET email=$2, name=$3, is_active=$4",
            user.id, user.email, user.name, user.is_active)
        return user

Directory Structure

app/
├── domain/           # Entities, value objects, ports (interfaces)
│   ├── entities/
│   ├── value_objects/
│   └── interfaces/
├── use_cases/        # Application business rules
├── adapters/         # Port implementations (DB, APIs, messaging)
│   ├── repositories/
│   ├── controllers/
│   └── gateways/
└── infrastructure/   # Framework config, logging, DI wiring

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