Layered Architecture — *Dividing Responsibilities into Layers*
Layered Architecture — *Dividing Responsibilities into Layers*
🎯 After reading this lesson
By the end of this lesson, you will be able to confidently do the following 3 things.
- ▸✅ Separate responsibilities across the Controller → Service → Repository 3-tier layers
- ▸✅ Know the exact location where
@Transactionalshould be applied - ▸✅ Understand who owns the responsibility of converting between DTOs and Entities
Keep the learning objectives as a checklist — close the lesson once you can answer all of them.
Why split into *3 layers*
The Core in One Line
A Spring backend typically divides code into 3 layers: Controller → Service → Repository. This is the standard approach for giving each layer a clear responsibility and producing maintainable code.
The Role of Each Layer
Controller: The HTTP gateway. Receives requests, validates, builds responses. No business logic.
Service: The heart of business logic. The unit of transaction. Coordinates multiple Repositories and external APIs.
Repository: Talks to the DB. Queries only. No business decisions.
Why split this way
1. Separation of concerns: Each layer does one thing well. If the Controller also runs DB queries, things become a mess.
2. Testability: When testing the Service, you can run unit tests without a real Controller or Repository — replace them with mocks.
3. Change isolation: If HTTP changes to GraphQL, you only modify the Controller. Service and Repository stay the same.
4. Transaction boundary: Attaching @Transactional to Service methods is the standard. Putting it on Controller or Repository creates ambiguous boundaries.
Real-world example
The Controller is thin, the Service is thick with business rules, and the Repository handles only DB access — a clean structure.
DTO — Object that carries data between layers
Layers do not pass the same object directly. They convert through DTOs (Data Transfer Objects).
- ▸CreateUserDto — request body
- ▸User — JPA Entity (DB mapping)
- ▸UserDto — response object
Why keep them separate?
- ▸The Entity's internal structure is not exposed externally (security and encapsulation)
- ▸Sensitive fields like passwords are naturally excluded from responses
- ▸API changes have no impact on the Entity (loose coupling)
Java 14+ record is fantastic for creating DTOs:
Common Anti-patterns
1. Controller handles business logic: Breaks transactions and prevents reuse
2. Service accepts HttpServletRequest: Creates HTTP dependency, making testing and reuse difficult
3. Business rules in Repository: The same query ends up written elsewhere too
4. Returning the Entity directly: Exposes passwords and couples internal structure to the outside
Summary
The 3-layer architecture is the Spring standard. Responsibilities are divided so each layer does one thing well. Using DTOs to carry data between layers reduces coupling.
🤖 Try asking AI like this
Knowing the concepts from this lesson lets you give AI specific instructions. Instead of a vague 'fix this', you make requests with vocabulary — that's the starting point for saving tokens.
- ▸"Apply the Layered Architecture — Dividing Responsibilities into Layers pattern to this Spring Boot code"
- ▸"Write a
@SpringBootTestintegration test related to Layered Architecture — Dividing Responsibilities into Layers" - ▸"Tell me 3 pitfalls to watch out for when using Layered Architecture — Dividing Responsibilities into Layers in production"
Why does this save tokens
Without knowing the concept, even after getting an AI response you have to ask 'What does that mean?' again. That follow-up question is what eats up tokens. Learn the concept once and the conversation ends in one go.