| name | clean-architecture |
| description | Apply Clean Architecture boundaries to keep domain logic isolated from frameworks and infrastructure. |
| origin | FlowDeck |
clean-architecture
When to Activate
When designing or implementing a new feature or service that needs clear separation of concerns, testability, and independence from frameworks, databases, or UI libraries.
Steps
- Identify the core business logic - Determine the essential domain rules that would exist even if the application had no UI, database, or external services.
- Define the boundary - Draw a clear boundary between the inner circles (entities, use cases) and outer circles (interfaces, infrastructure).
- Place dependencies pointing inward - Dependencies should always point toward the center. The inner circle knows nothing about the outer circle.
- Define ports (interfaces) - Create interfaces in the domain layer that define how the outside world can interact with it.
- Implement adapters - Create concrete implementations (adapters) for databases, web frameworks, external APIs, etc. in the outer layers.
- Wire everything via dependency injection - Use a composition root or DI container to assemble the application.
Examples
class Order {
constructor(
private readonly id: string,
private readonly items: OrderItem[],
private readonly status: OrderStatus
) {}
get total(): number {
return this.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
}
canBeFulfilled(): boolean {
return this.status === 'pending' && this.items.length > 0
}
}
interface OrderRepository {
findById(id: string): Promise<Order | null>
save(order: Order): Promise<void>
}
interface NotificationService {
sendOrderConfirmation(order: Order): Promise<void>
}
class PlaceOrderUseCase {
constructor(
private readonly orderRepo: OrderRepository,
private readonly notifier: NotificationService
) {}
async execute(orderData: OrderData): Promise<Order> {
const order = new Order(orderData.id, orderData.items, 'pending')
if (!order.canBeFulfilled()) {
throw new InvalidOrderError('Order cannot be fulfilled')
}
await this.orderRepo.save(order)
await this.notifier.sendOrderConfirmation(order)
return order
}
}
class PostgresOrderRepository implements OrderRepository {
async findById(id: string): Promise<Order | null> {
}
async save(order: Order): Promise<void> {
}
}
class EmailNotificationService implements NotificationService {
async sendOrderConfirmation(order: Order): Promise<void> {
}
}
Related Skills
- layered-architecture
- hexagonal-architecture
- ddd-architecture
- backend-patterns