0
0
Spring Bootframework~15 mins

Service calling repository in Spring Boot - Deep Dive

Choose your learning style9 modes available
Overview - Service calling repository
What is it?
In Spring Boot, a service calling a repository means that a service class uses a repository class to get or save data. The repository handles the database operations, while the service contains the business logic. This separation helps keep the code organized and easier to maintain. The service acts like a middleman between the user requests and the database.
Why it matters
Without this separation, the code would mix business rules and database details, making it hard to change or fix. If the service directly handled database code, it would be messy and error-prone. Using a repository lets developers focus on business logic in the service and database work in the repository, improving clarity and reducing bugs.
Where it fits
Before learning this, you should understand basic Java classes and methods, and how Spring Boot manages components. After this, you can learn about advanced service patterns, transaction management, and how to test services and repositories separately.
Mental Model
Core Idea
A service calls a repository to separate business logic from data access, making the application cleaner and easier to manage.
Think of it like...
Think of a restaurant: the service is the waiter who takes your order and explains what you want, while the repository is the kitchen that actually prepares the food. The waiter doesn’t cook, and the kitchen doesn’t talk to customers directly.
┌─────────────┐       calls       ┌───────────────┐
│   Service   │ ───────────────▶ │  Repository   │
└─────────────┘                  └───────────────┘
       ▲                              │
       │                              ▼
   business                     database
    logic                      operations
Build-Up - 7 Steps
1
FoundationUnderstanding service and repository roles
🤔
Concept: Learn what a service and a repository are and their responsibilities.
A service contains the business rules and logic of your application. It decides what to do with data. A repository is responsible for talking to the database. It saves, finds, updates, or deletes data. In Spring Boot, repositories often extend interfaces like JpaRepository to get ready-made database methods.
Result
You know that services handle logic and repositories handle data storage separately.
Understanding the clear roles of service and repository helps you organize code so each part does one job well.
2
FoundationCreating a simple repository interface
🤔
Concept: How to define a repository interface in Spring Boot for data access.
In Spring Boot, you create a repository by making an interface that extends JpaRepository or CrudRepository. For example: public interface UserRepository extends JpaRepository {} This interface automatically gets methods like save(), findById(), and delete(). You don’t write the code for these methods yourself.
Result
You have a ready-to-use repository interface that can perform basic database operations.
Knowing that Spring Boot generates database code for you saves time and reduces errors.
3
IntermediateInjecting repository into service
🤔Before reading on: do you think the service creates the repository instance itself or Spring Boot provides it? Commit to your answer.
Concept: Learn how to connect the repository to the service using dependency injection.
In Spring Boot, you don’t create repository objects manually. Instead, you ask Spring to provide them. You do this by adding a field in your service class and marking it with @Autowired or using constructor injection: @Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } // business methods } Spring automatically creates the UserRepository object and passes it to the service.
Result
The service has access to the repository without manually creating it.
Understanding dependency injection helps you write cleaner, testable code without manual object creation.
4
IntermediateCalling repository methods from service
🤔Before reading on: do you think the service should handle database queries directly or call repository methods? Commit to your answer.
Concept: How the service uses repository methods to perform data operations.
Inside the service, you call repository methods to get or save data. For example: public User getUserById(Long id) { return userRepository.findById(id).orElse(null); } public User saveUser(User user) { return userRepository.save(user); } The service can add extra logic before or after calling the repository, like checking conditions or transforming data.
Result
The service handles business logic and uses the repository for data access.
Knowing that the service delegates data tasks to the repository keeps responsibilities clear and code maintainable.
5
IntermediateCustom repository queries for complex needs
🤔Before reading on: do you think repositories can only use built-in methods or can they have custom queries? Commit to your answer.
Concept: Repositories can have custom queries for special data retrieval needs.
Sometimes built-in methods are not enough. You can add custom queries in repositories using @Query annotation or method naming conventions: @Query("SELECT u FROM User u WHERE u.email = ?1") User findByEmail(String email); Or: User findByLastName(String lastName); The service calls these methods just like the built-in ones.
Result
You can handle complex data queries cleanly through the repository.
Knowing how to add custom queries lets you keep database logic in one place without cluttering the service.
6
AdvancedTransaction management in service layer
🤔Before reading on: do you think transactions should be managed in the repository or service? Commit to your answer.
Concept: Services often manage transactions to ensure data consistency across multiple repository calls.
In Spring Boot, you use @Transactional on service methods to group database operations into one transaction. If one operation fails, all changes roll back: @Transactional public void updateUserAndLog(User user) { userRepository.save(user); logRepository.save(new Log(...)); } This ensures data stays consistent.
Result
Your service can safely perform multiple data operations as one unit.
Understanding transaction management in services prevents partial updates and data corruption.
7
ExpertAvoiding common pitfalls with service-repository calls
🤔Before reading on: do you think calling repositories directly from controllers is a good practice? Commit to your answer.
Concept: Best practices and hidden traps when services call repositories in real projects.
Calling repositories directly from controllers breaks separation of concerns and makes testing harder. Also, beware of lazy loading issues when accessing data outside transactions. Use service methods to control data access and transactions. Avoid putting business logic in repositories or controllers. Keep services focused on business rules and repository on data access.
Result
Your application stays clean, testable, and less error-prone.
Knowing these pitfalls helps you design robust applications and avoid subtle bugs.
Under the Hood
Spring Boot uses dependency injection to create and provide repository instances to services. Repositories are proxies that implement data access methods, often using JPA or other persistence frameworks. When a service calls a repository method, the proxy translates it into database queries. Transactions are managed by Spring’s transaction manager, which wraps service methods to start, commit, or rollback database transactions automatically.
Why designed this way?
This design separates concerns: business logic is kept in services, and data access in repositories. It allows easier testing, maintenance, and swapping of database implementations. Dependency injection reduces manual wiring and promotes loose coupling. Transaction management at the service level ensures consistent data changes across multiple operations.
┌─────────────┐      injects      ┌───────────────┐
│   Service   │ ───────────────▶ │  Repository   │
│ (business)  │                  │ (data access) │
└─────┬───────┘                  └──────┬────────┘
      │                                  │
      │ calls                            │ translates
      ▼                                  ▼
┌─────────────┐                  ┌───────────────┐
│ Transaction │                  │ Database      │
│  Manager    │                  │ (SQL/NoSQL)   │
└─────────────┘                  └───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Should controllers call repositories directly or go through services? Commit to your answer.
Common Belief:Controllers can call repositories directly to simplify code.
Tap to reveal reality
Reality:Controllers should call services, not repositories, to keep business logic separate and maintainable.
Why it matters:Direct repository calls in controllers mix concerns, making code harder to test and maintain.
Quick: Do repositories contain business logic? Commit to your answer.
Common Belief:Repositories can have business rules mixed with data access.
Tap to reveal reality
Reality:Repositories should only handle data access; business logic belongs in services.
Why it matters:Mixing logic in repositories leads to tangled code and harder debugging.
Quick: Does Spring Boot create repository instances automatically or do you need to instantiate them? Commit to your answer.
Common Belief:You must create repository objects manually in your code.
Tap to reveal reality
Reality:Spring Boot automatically creates and injects repository instances using dependency injection.
Why it matters:Manually creating repositories breaks Spring’s lifecycle and can cause errors.
Quick: Can you safely access lazy-loaded data outside a transaction? Commit to your answer.
Common Belief:Lazy-loaded data can be accessed anytime without issues.
Tap to reveal reality
Reality:Lazy-loaded data requires an active transaction; otherwise, it causes errors.
Why it matters:Ignoring this causes runtime exceptions and data access failures.
Expert Zone
1
Services can orchestrate multiple repositories and external calls, acting as a business process coordinator, not just a simple pass-through.
2
Using constructor injection for repositories in services improves immutability and makes unit testing easier compared to field injection.
3
Transaction boundaries at the service layer allow fine control over rollback behavior and performance tuning, which is critical in complex applications.
When NOT to use
Avoid using service-repository separation for very simple applications or scripts where adding layers adds unnecessary complexity. In such cases, direct repository use or simpler DAO patterns may suffice.
Production Patterns
In real projects, services often implement interfaces for easier mocking in tests. Repositories use custom queries and projections for performance. Services handle caching, validation, and error handling, while repositories remain focused on efficient data access.
Connections
Dependency Injection
Service calling repository relies on dependency injection to provide repository instances.
Understanding dependency injection clarifies how services get repositories without manual creation, improving modularity.
Transaction Management
Services manage transactions that cover multiple repository calls to ensure data consistency.
Knowing transaction management helps understand why service methods are often annotated with @Transactional.
Separation of Concerns (Software Design)
Service and repository layers separate business logic from data access, following this design principle.
Recognizing this principle helps appreciate why layered architecture improves maintainability and testability.
Common Pitfalls
#1Calling repository methods directly from controllers.
Wrong approach:@RestController public class UserController { @Autowired private UserRepository userRepository; @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { return userRepository.findById(id).orElse(null); } }
Correct approach:@RestController public class UserController { private final UserService userService; public UserController(UserService userService) { this.userService = userService; } @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { return userService.getUserById(id); } }
Root cause:Misunderstanding the role of services leads to mixing business logic in controllers.
#2Mixing business logic inside repository methods.
Wrong approach:public interface UserRepository extends JpaRepository { default boolean isUserActive(Long id) { User user = findById(id).orElse(null); return user != null && user.isActive(); } }
Correct approach:public interface UserRepository extends JpaRepository {} @Service public class UserService { private final UserRepository userRepository; public boolean isUserActive(Long id) { User user = userRepository.findById(id).orElse(null); return user != null && user.isActive(); } }
Root cause:Confusing data access with business rules causes logic to leak into repositories.
#3Not using @Transactional on service methods that modify data.
Wrong approach:@Service public class UserService { private final UserRepository userRepository; public void updateUser(User user) { userRepository.save(user); // no transaction annotation } }
Correct approach:@Service @Transactional public class UserService { private final UserRepository userRepository; public void updateUser(User user) { userRepository.save(user); } }
Root cause:Lack of transaction management leads to partial updates and data inconsistency.
Key Takeaways
Services and repositories have distinct roles: services handle business logic, repositories handle data access.
Spring Boot uses dependency injection to provide repository instances to services automatically.
Services call repository methods to perform database operations, keeping code organized and maintainable.
Transaction management is usually done in the service layer to ensure data consistency across multiple operations.
Avoid mixing business logic in repositories or calling repositories directly from controllers to keep a clean architecture.