QTPortfolio
Clean Architecture in Java: Organizing a Spring Boot Project
JavaSpring BootArchitectureBest Practices

Clean Architecture in Java: Organizing a Spring Boot Project

April 6, 20261 min read~177 words

The Problem with MVC Spaghetti

When a Spring Boot project grows, it's tempting to put business logic in controllers or let entities bleed into API responses. This leads to tightly coupled, untestable code. Clean Architecture solves this.

Layer Separation

com.portfolio/
├── controller/   → HTTP layer (receives requests, returns responses)
├── service/      → Business logic (the core of your app)
├── repository/   → Data access (JPA repositories)
├── entity/       → JPA entities (maps to DB tables)
├── dto/
│   ├── request/  → What the API receives (validation here)
│   └── response/ → What the API returns (never expose entities)
└── exception/    → Global error handling

DTOs as API Contracts

Never return JPA entities directly from your controllers. Use DTOs to control exactly what data is exposed.

Global Exception Handling

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ApiResponse<Void>> handleNotFound(
            ResourceNotFoundException ex) {
        return ResponseEntity.status(404)
            .body(ApiResponse.error(ex.getMessage()));
    }
}

Why This Matters

With this structure, you can swap your database without touching a single controller. You can unit test service classes in isolation. And onboarding new developers becomes much easier.

Enjoyed this article?

Share it with your network or explore more posts below.