Clean / Hexagonal Architecture — Dependencies Point Inward
Business logic has zero dependencies on infrastructure. Frameworks, ORMs, and HTTP live outside the domain.
When to use
- Apps that need swappable infrastructure (swap DB, messaging, HTTP without touching domain)
- Maximizing testability of business logic in isolation
Tradeoffs
- More interfaces and indirection (verbose wiring)
- Over-engineered for simple CRUD apps
- Go
- Python
// domain/order.go — zero framework imports
package domain
type OrderRepo interface {
Save(o Order) error
FindByID(id string) (Order, error)
}
type PlaceOrderUseCase struct{ repo OrderRepo }
func (uc *PlaceOrderUseCase) Execute(cmd PlaceOrderCmd) error {
order := NewOrder(cmd.CustomerID, cmd.Items)
return uc.repo.Save(order)
}
# domain/order.py — zero framework imports
from abc import ABC, abstractmethod
class OrderRepo(ABC):
@abstractmethod
def save(self, order: Order) -> None: ...
@abstractmethod
def find_by_id(self, id: str) -> Order: ...
class PlaceOrderUseCase:
def __init__(self, repo: OrderRepo) -> None:
self._repo = repo
def execute(self, cmd: PlaceOrderCmd) -> None:
order = Order.create(cmd.customer_id, cmd.items)
self._repo.save(order)
Gotcha: If your domain layer imports a framework, an ORM, or an HTTP package — it's not Clean Architecture.