Clean
Clean Architecture, proposed by Robert C. Martin (Uncle Bob), organizes a system into concentric layers with the most important business rules at the center and less important technical details on the outside.
The core idea:
Dependencies must always point inward toward the business rules.
This ensures:
- Business logic is independent of UI, databases, frameworks, and external services.
- External technologies can be replaced without affecting the core.
The Four Main Layersβ
From inside β outside:
Entities (Enterprise Business Rules)β
- Represent core business objects and rules.
- Are independent of application logic and technology.
Examples: User, Order, Invoice, Account.
Use Cases (Application Business Rules)β
- Orchestrate how entities are used.
- Contain application-specific business rules.
- Define what the system can do.
Examples: PlaceOrder, TransferMoney, RegisterUser.
Interface Adaptersβ
- Convert data between formats required by:
- UI β Use Cases
- Use Cases β Database/External services
- Includes controllers, presenters, gateways, repositories.
Frameworks & Driversβ
- Outermost layer.
- Contains UI frameworks, databases, web servers, messaging systems, and external APIs.
- These are replaceable details.
Dependency Ruleβ
All source code dependencies must point inward.
That means:
- Use cases depend on entities.
- Interface adapters depend on use cases.
- Frameworks depend on interface adapters.
- No inward layer depends on an outward layer.
Example: Online Ordering Systemβ
Letβs design a Place Order feature using Clean Architecture.
1. Entities Layerβ
public class Order {
private int customerId;
private List<Item> items;
public Order(int customerId, List<Item> items) {
this.customerId = customerId;
this.items = items;
}
public double calculateTotal() {
return items.stream().mapToDouble(Item::getPrice).sum();
}
}
Pure business object, no frameworks, no DB, no UI.
2. Use Cases Layerβ
Input Boundary (Use Case Interface)β
public interface PlaceOrderInputBoundary {
void execute(PlaceOrderRequestModel request);
}
Request Modelβ
public class PlaceOrderRequestModel {
public int customerId;
public List<Item> items;
}
Use Case Interactorβ
public class PlaceOrderInteractor implements PlaceOrderInputBoundary {
private final OrderGateway orderGateway;
private final PaymentGateway paymentGateway;
public PlaceOrderInteractor(OrderGateway orderGateway, PaymentGateway paymentGateway) {
this.orderGateway = orderGateway;
this.paymentGateway = paymentGateway;
}
@Override
public void execute(PlaceOrderRequestModel request) {
Order order = new Order(request.customerId, request.items);
paymentGateway.processPayment(order);
orderGateway.save(order);
}
}
Coordinates entities and business rules, but knows nothing about frameworks or databases.
3. Interface Adapters Layerβ
Output Boundary (Presenter Interface)β
public interface PlaceOrderOutputBoundary {
void present(PlaceOrderResponseModel response);
}
Response Modelβ
public class PlaceOrderResponseModel {
public String status;
}
Controllerβ
public class PlaceOrderController {
private final PlaceOrderInputBoundary inputBoundary;
public PlaceOrderController(PlaceOrderInputBoundary inputBoundary) {
this.inputBoundary = inputBoundary;
}
public void handleRequest(int customerId, List<Item> items) {
PlaceOrderRequestModel request = new PlaceOrderRequestModel();
request.customerId = customerId;
request.items = items;
inputBoundary.execute(request);
}
}
Converts UI input into a format the use case understands.
Gateways (Interfaces)β
public interface OrderGateway {
void save(Order order);
}
public interface PaymentGateway {
void processPayment(Order order);
}
These are defined inward, but implemented outward.
4. Frameworks & Drivers Layerβ
Database Implementationβ
public class JpaOrderGateway implements OrderGateway {
@Override
public void save(Order order) {
// Save using JPA / SQL
}
}
Payment API Adapterβ
public class StripePaymentGateway implements PaymentGateway {
@Override
public void processPayment(Order order) {
// Call Stripe API
}
}
Web UI Controller (Framework)β
@RestController
public class OrderRestController {
private final PlaceOrderController controller;
@PostMapping("/orders")
public void placeOrder(@RequestBody OrderRequestDTO dto) {
controller.handleRequest(dto.getCustomerId(), dto.getItems());
}
}
These are replaceable technical details.
How the Flow Worksβ
- User sends HTTP request β Web controller.
- Web controller calls application controller.
- Controller creates request model β passes to use case.
- Use case creates entity β processes business rules.
- Use case calls gateway interfaces.
- Gateways are implemented by infrastructure adapters.
- Response flows back outward.
Advantages of Clean Architecture
- Business logic independent of frameworks
- Highly testable (mock boundaries)
- Easy to change UI, database, or external services
- Encourages SOLID principles
- Long-term maintainability
Disadvantages
- More boilerplate code
- Steeper learning curve
- Overkill for very small projects
Clean Architecture vs Hexagonal vs Layeredβ
| Feature | Layered | Hexagonal | Clean Architecture |
|---|---|---|---|
| Structure | Horizontal layers | Core + ports/adapters | Concentric rings |
| Dependency Direction | Top β Down | Inward | Inward |
| Core independent of frameworks | β Sometimes | β Yes | β Yes |
| Emphasis | Technical separation | Boundary isolation | Business rules first |