Clean Architecture
Learn Clean Architecture from the ground up. Understand the Dependency Rule, Layers, Domain Model, Use Cases, Adapters, Frameworks, SOLID principles, Spring Boot implementation, Hexagonal Architecture comparison, and real-world enterprise examples from Banking, Amazon, Netflix, Uber, and Microservices.
Introduction
Imagine you're building an Online Banking Application.
The application supports:
- Customer Registration
- Account Management
- Money Transfer
- Loan Processing
- Credit Cards
- Notifications
Initially, everything is implemented inside controllers.
@RestController
public class TransferController {
@Autowired
private JdbcTemplate jdbcTemplate;
@PostMapping("/transfer")
public String transfer() {
// Validation
// Business Logic
// SQL Query
// Notification
return "Success";
}
}
Everything is mixed together.
Problems appear quickly:
- ❌ Difficult to test
- ❌ Tight coupling
- ❌ Business logic depends on Spring Boot
- ❌ Database changes affect business rules
- ❌ Hard to migrate technologies
- ❌ Low maintainability
Modern enterprise systems solve this using Clean Architecture.
Learning Objectives
After completing this article, you'll understand:
- What is Clean Architecture?
- Why Clean Architecture?
- Dependency Rule
- Four Layers
- Entities
- Use Cases
- Interface Adapters
- Frameworks & Drivers
- Dependency Injection
- SOLID Principles
- Spring Boot Implementation
- Comparison with Layered Architecture
- Hexagonal Architecture
- Best Practices
What is Clean Architecture?
Clean Architecture is an architectural pattern proposed by Robert C. Martin (Uncle Bob).
Its primary goal is:
Business Rules should not depend on frameworks, databases, or external technologies.
Instead,
everything depends towards the center.
Traditional Layered Architecture
flowchart TD
CLIENT[Client]
CONTROLLER[Controller]
SERVICE[Service]
REPOSITORY[Repository]
DATABASE[(Database)]
CLIENT --> CONTROLLER
CONTROLLER --> SERVICE
SERVICE --> REPOSITORY
REPOSITORY --> DATABASE
The business layer often becomes tightly coupled to Spring Boot and JPA.
Clean Architecture
flowchart TD
FRAMEWORK[Frameworks & Drivers]
ADAPTERS[Interface Adapters]
USECASE[Use Cases]
ENTITY[Entities]
FRAMEWORK --> ADAPTERS
ADAPTERS --> USECASE
USECASE --> ENTITY
Dependencies always point inward.
The Dependency Rule
The most important rule.
Outer Layers
↓
Depend On
↓
Inner Layers
Never the opposite.
The Domain must never know:
- Spring Boot
- Hibernate
- PostgreSQL
- Kafka
- REST
- Redis
Core Layers
flowchart LR
E[Entities]
UC[Use Cases]
IA[Interface Adapters]
FW[Frameworks]
FW --> IA
IA --> UC
UC --> E
Layer 1 — Entities
The center of the architecture.
Contains
- Business Rules
- Domain Models
- Validation
- Business Policies
Example
public class Account {
private BigDecimal balance;
public void withdraw(BigDecimal amount){
if(balance.compareTo(amount) < 0){
throw new RuntimeException("Insufficient Balance");
}
balance = balance.subtract(amount);
}
}
Notice
No Spring annotations.
No JPA annotations.
Pure Java.
Why Entities Should Be Independent?
Entities should survive even if tomorrow you replace:
- Spring Boot → Micronaut
- PostgreSQL → MongoDB
- REST → GraphQL
Business logic remains unchanged.
Layer 2 — Use Cases
Use Cases contain
Application Business Logic.
Example
Transfer Money
↓
Validate Account
↓
Withdraw
↓
Deposit
↓
Save
Use Case Flow
flowchart TD
REQUEST[Transfer Request]
VALIDATE[Validate]
WITHDRAW[Withdraw Money]
DEPOSIT[Deposit Money]
SAVE[Persist]
REQUEST --> VALIDATE
VALIDATE --> WITHDRAW
WITHDRAW --> DEPOSIT
DEPOSIT --> SAVE
Example Use Case
public class TransferMoneyUseCase {
private AccountRepository repository;
public void execute(TransferRequest request){
// Business Logic
}
}
Notice
No Spring Boot annotations.
No REST.
Layer 3 — Interface Adapters
Adapters convert external requests into use cases.
Examples
- REST Controllers
- GraphQL
- Kafka Consumers
- JPA Repositories
- DTO Mappers
Adapter Flow
flowchart LR
REST[REST Controller]
DTO[DTO Mapper]
USECASE[Use Case]
REST --> DTO
DTO --> USECASE
REST Controller
@RestController
public class TransferController {
@PostMapping("/transfer")
public ResponseEntity<?> transfer(){
useCase.execute();
return ResponseEntity.ok().build();
}
}
Controller only delegates.
Business logic stays in Use Cases.
Repository Interface
Inside the Domain
public interface AccountRepository{
void save(Account account);
Account find(Long id);
}
The domain defines the contract.
Repository Implementation
Infrastructure Layer
@Repository
public class JpaAccountRepository
implements AccountRepository{
}
JPA is hidden behind the interface.
Layer 4 — Frameworks & Drivers
Outer layer.
Contains
- Spring Boot
- Hibernate
- PostgreSQL
- Kafka
- Redis
- REST APIs
- AWS SDK
These are implementation details.
Complete Request Flow
sequenceDiagram
participant Client
participant Controller
participant UseCase
participant Repository
participant Database
Client->>Controller: POST /transfer
Controller->>UseCase: execute()
UseCase->>Repository: save()
Repository->>Database: INSERT
Database-->>Repository: Success
Repository-->>UseCase: Saved
UseCase-->>Controller: Success
Controller-->>Client: 200 OK
Dependency Injection
Spring Boot wires dependencies.
flowchart LR
SPRING[Spring Container]
CONTROLLER[Controller]
USECASE[Use Case]
REPOSITORY[Repository]
SPRING --> CONTROLLER
SPRING --> USECASE
SPRING --> REPOSITORY
Business logic remains framework independent.
Spring Boot Folder Structure
src
├── domain
│ ├── entity
│ ├── repository
│ └── usecase
│
├── application
│
├── infrastructure
│ ├── persistence
│ ├── kafka
│ ├── redis
│ └── security
│
├── web
│ ├── controller
│ └── dto
│
└── config
Example Banking Architecture
flowchart TD
CLIENT[Customer]
API[REST API]
USECASE[Transfer Money]
DOMAIN[Account Entity]
JPA[JPA Repository]
DB[(PostgreSQL)]
CLIENT --> API
API --> USECASE
USECASE --> DOMAIN
USECASE --> JPA
JPA --> DB
Clean Architecture vs Layered Architecture
| Layered | Clean |
|---|---|
| Framework-centric | Domain-centric |
| Business depends on Spring | Spring depends on Business |
| Difficult to replace DB | Easy to replace DB |
| Lower Testability | High Testability |
| Tighter Coupling | Loose Coupling |
Clean Architecture vs Hexagonal Architecture
| Clean Architecture | Hexagonal Architecture |
|---|---|
| Uncle Bob | Alistair Cockburn |
| Concentric Layers | Ports & Adapters |
| Dependency Rule | Dependency Inversion |
| Focus on Use Cases | Focus on Ports |
Both share similar goals.
SOLID Principles
Clean Architecture relies heavily on SOLID.
- Single Responsibility
- Open Closed
- Liskov Substitution
- Interface Segregation
- Dependency Inversion
Especially
Dependency Inversion Principle
Spring Boot Integration
flowchart TD
CLIENT[React]
CONTROLLER[REST Controller]
USECASE[Application Service]
DOMAIN[Domain Model]
POSTGRES[(PostgreSQL)]
KAFKA[(Kafka)]
REDIS[(Redis)]
CLIENT --> CONTROLLER
CONTROLLER --> USECASE
USECASE --> DOMAIN
USECASE --> POSTGRES
USECASE --> KAFKA
USECASE --> REDIS
Banking Example
Transfer Money
Controller
↓
Transfer Use Case
↓
Account Entity
↓
Repository
↓
Database
Business rules never depend on Spring.
Amazon Example
Each microservice keeps its core business logic independent of infrastructure, making it easier to evolve technologies without changing domain rules.
Netflix Example
Streaming services isolate recommendation logic from delivery infrastructure, allowing teams to change databases, messaging platforms, or deployment models independently.
Uber Example
Ride pricing, dispatch, and payment logic remain separate from REST APIs, databases, and messaging systems.
Advantages
- High Testability
- Framework Independence
- Database Independence
- UI Independence
- Easier Maintenance
- Better Separation of Concerns
- Long-Term Flexibility
Challenges
- More Classes
- Initial Learning Curve
- More Interfaces
- Additional Boilerplate
- Requires Strong Design Discipline
Monitoring
Monitor
- API Response Time
- Use Case Execution Time
- Database Calls
- Repository Performance
- Kafka Latency
- Redis Latency
- Exception Rates
Tools
- Prometheus
- Grafana
- Datadog
- OpenTelemetry
- Jaeger
Common Mistakes
❌ Putting business logic inside controllers
❌ Using JPA entities as domain entities
❌ Domain depending on Spring annotations
❌ Direct database access from controllers
❌ Large service classes ("God Services")
❌ Mixing DTOs with domain models
Best Practices
- Keep entities as plain Java objects.
- Place business rules in use cases, not controllers.
- Define repository interfaces in the domain layer.
- Keep infrastructure replaceable.
- Use dependency injection for wiring.
- Treat frameworks as implementation details.
- Write unit tests for use cases without requiring Spring Boot.
Common Interview Questions
What is Clean Architecture?
Clean Architecture organizes software so that business rules remain independent of frameworks, databases, and external technologies.
What is the Dependency Rule?
Dependencies always point toward the inner layers. Inner layers never depend on outer layers.
Why are entities kept framework independent?
So business rules remain reusable, testable, and unaffected by infrastructure changes.
What belongs in the Use Case layer?
Application-specific business logic, orchestration of domain entities, and coordination of repositories.
Is Clean Architecture suitable for Microservices?
Yes. Each microservice can internally follow Clean Architecture, combining independent deployment with maintainable domain logic.
Summary
Clean Architecture helps build maintainable, testable, and technology-independent software by placing business rules at the center and treating frameworks, databases, and messaging systems as replaceable implementation details.
In this article, we covered:
- Clean Architecture fundamentals
- Dependency Rule
- Four architectural layers
- Entities
- Use Cases
- Interface Adapters
- Frameworks & Drivers
- Spring Boot implementation
- SOLID principles
- Comparison with Layered and Hexagonal Architecture
- Banking, Amazon, Netflix, and Uber examples
- Best practices
Clean Architecture is widely used in enterprise Java applications because it enables long-term maintainability, easier testing, and flexibility to evolve technologies without rewriting business logic.
Comments
Share a question, correction, or practical insight about this article.
Checking login status...
Loading approved comments...