Lesson 49 • Expert
Clean Architecture
Design maintainable, testable Java applications using Clean Architecture, SOLID principles, and domain-driven design.
Before You Start
You should understand OOP Design Patterns, Interfaces & Abstraction, and Unit Testing. Familiarity with Generics and Exception Architecture also helps.
What You'll Learn
- ✅ Clean Architecture layers and the dependency rule
- ✅ SOLID principles with real Java examples
- ✅ Domain-Driven Design: Entities, Value Objects, Aggregates
- ✅ Hexagonal Architecture (Ports & Adapters)
- ✅ Use Cases as the heart of your application
- ✅ Package structure for large Java projects
1️⃣ The Dependency Rule
Analogy: Think of Clean Architecture like an onion. The innermost layer is your domain — pure business logic with no knowledge of databases, web frameworks, or UI. Each outer layer can depend on inner layers, but never the reverse. Your domain doesn't care if data comes from PostgreSQL or a CSV file.
Why it matters: When your domain is framework-free, you can test business logic without Spring, swap databases without touching business rules, and understand the system by reading the domain layer alone.
| Layer | Contains | Depends On | Example |
|---|---|---|---|
| Domain | Entities, Value Objects, Events | Nothing! | Order, Money, OrderPlaced |
| Application | Use Cases, Ports (interfaces) | Domain only | PlaceOrderUseCase |
| Infrastructure | DB repos, API clients, messaging | App + Domain | JpaOrderRepository |
| Presentation | Controllers, DTOs, view models | App + Domain | OrderController |
Try It: Domain Layer — Entities & Value Objects
// 💡 Try modifying this code and see what happens!
// Domain Layer — pure business logic, ZERO dependencies
console.log("=== Domain Layer ===\n");
// 1. Value Object — immutable, compared by value
console.log("1. VALUE OBJECT (Money):");
class Money {
constructor(amount, currency) {
if (amount < 0) throw new Error("Amount cannot be negative");
if (!currency) throw new Error("Currency is required");
this.amount = amount;
this.currency = currency;
Object.freeze(this); // Im
...2️⃣ SOLID Principles
SOLID isn't abstract theory — it's a practical checklist for writing code that's easy to change, test, and extend. Every principle answers a specific question about your design.
| Principle | Question It Answers | Violation Sign |
|---|---|---|
| S — Single Responsibility | Does this class have only one reason to change? | Class >300 lines |
| O — Open/Closed | Can I add behavior without modifying existing code? | Giant if/switch chains |
| L — Liskov Substitution | Can subtypes replace parent types safely? | instanceof checks |
| I — Interface Segregation | Do clients depend only on methods they use? | Empty method implementations |
| D — Dependency Inversion | Do high-level modules depend on abstractions? | new JpaRepo() in service |
Try It: Application Layer — Use Cases & Ports
// 💡 Try modifying this code and see what happens!
// Application Layer — Use Cases and Ports
console.log("=== Application Layer ===\n");
// 1. Port (interface) — defined in application layer
console.log("1. PORTS (Interfaces):");
console.log(` // Output Port: what the app needs from infrastructure
public interface OrderRepository {
Order findById(OrderId id);
void save(Order order);
List<Order> findByStatus(OrderStatus status);
}
// Output Port: external service
pu
...Try It: Infrastructure & Package Structure
// 💡 Try modifying this code and see what happens!
// Infrastructure adapters and package structure
console.log("=== Infrastructure & Package Structure ===\n");
// 1. Infrastructure adapters
console.log("1. INFRASTRUCTURE ADAPTERS:");
console.log(` // JPA adapter — implements the port
@Repository
public class JpaOrderRepository implements OrderRepository {
private final SpringDataOrderRepo springRepo;
@Override
public Order findById(OrderId id) {
return spring
...Common Beginner Mistakes
- ❌ Anemic domain model — entities with only getters/setters and all logic in services. Put business rules IN the domain objects where they belong
- ❌ Leaking infrastructure into domain — JPA annotations (
@Entity,@Column) on domain objects couples your domain to Hibernate. Use separate persistence entities - ❌ Over-engineering for small apps — a CRUD app with 3 tables doesn't need 4 layers. Clean Architecture shines in complex domains with many business rules
- ❌ Skipping the application layer — controllers calling repositories directly. Use Cases coordinate the workflow and are the natural place for transaction boundaries
- ❌ Bidirectional dependencies — if your domain imports from infrastructure, the dependency rule is violated. Dependencies always point inward
Pro Tips
- 💡 ArchUnit — enforce architecture rules in tests: domain must not depend on infrastructure. Tests fail if someone violates the dependency rule
- 💡 Records for Value Objects — Java records are perfect for VOs:
record Money(BigDecimal amount, Currency currency) {}— immutable by default! - 💡 Domain Events over direct coupling — instead of OrderService calling EmailService, publish an OrderPlaced event. The email handler subscribes independently
- 💡 Start simple — you don't need every DDD pattern on day one. Start with Entities + Repositories, add Value Objects and Events as complexity grows
📋 Quick Reference
| Concept | Pattern | Purpose |
|---|---|---|
| Ports | interface OrderRepository | Define boundaries (WHAT) |
| Adapters | JpaOrderRepo implements | Implementation (HOW) |
| Use Case | class PlaceOrderUseCase | Application workflow |
| Entity | class Order (with OrderId) | Identity + business rules |
| Value Object | record Money(amount, currency) | Immutable, equality by value |
| Aggregate | Order + OrderLines | Consistency boundary |
| Domain Event | record OrderPlaced(OrderId) | Cross-boundary communication |
🎉 Lesson Complete!
You've mastered Clean Architecture! You understand the dependency rule, SOLID principles, domain-driven design, and how Ports & Adapters keep your business logic framework-free. Next: Final Project — build a complete Java application applying everything you've learned across all 49 lessons.
Sign up for free to track which lessons you've completed and get learning reminders.