Какая область ответственности у Repository в Spring?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Область ответственности Repository в Spring
Repository — это один из ключевых паттернов в архитектуре приложений. В контексте Spring он имеет очень конкретную область ответственности и это главный слой взаимодействия с базой данных.
Основная ответственность Repository
Репозиторий отвечает исключительно за доступ к данным (Data Access Layer). Его основная задача:
- Получение данных из хранилища (БД, файлы, API и т.д.)
- Преобразование raw данных в domain объекты
- Сохранение domain объектов обратно в хранилище
- Удаление данных
- Поиск данных по различным критериям
// Repository ОТВЕЧАЕТ за работу с БД
public interface UserRepository extends JpaRepository<User, Long> {
// Это его область ответственности - работа с данными
User findByEmail(String email);
List<User> findByAgeGreaterThan(int age);
void deleteById(Long id);
}
Архитектурный паттерн
В современной архитектуре (Clean Architecture, DDD) слои выстраиваются так:
Presentation Layer (Controllers, Views)
↓
Application Layer (Services, Use Cases)
↓
Domain Layer (Business Logic, Entities)
↓
Infrastructure Layer (Repositories, Database)
Репозиторий находится в Infrastructure Layer и служит мостом между Application/Domain слоями и физическим хранилищем данных.
Что Repository должен делать
1. CRUD операции:
@Repository
public interface OrderRepository extends JpaRepository<Order, Long> {
// Create (сохранение)
Order save(Order order);
// Read (получение)
Optional<Order> findById(Long id);
List<Order> findAll();
// Update (через save)
// Delete
void deleteById(Long id);
}
2. Поиск по критериям:
public interface UserRepository extends JpaRepository<User, Long> {
// Поиск по одному критерию
List<User> findByStatus(UserStatus status);
// Поиск по нескольким критериям
List<User> findByStatusAndAge(UserStatus status, int age);
// Поиск с пагинацией
Page<User> findByStatus(UserStatus status, Pageable pageable);
}
3. Использование Specifications для сложных запросов:
public interface UserRepository extends JpaRepository<User, Long>,
JpaSpecificationExecutor<User> {
}
// В service слое
public class UserService {
private UserRepository userRepository;
public List<User> findUsers(int minAge, UserStatus status) {
Specification<User> spec = (root, query, cb) -> cb.and(
cb.greaterThanOrEqualTo(root.get("age"), minAge),
cb.equal(root.get("status"), status)
);
return userRepository.findAll(spec);
}
}
Что Repository НЕ должен делать
1. Бизнес-логика НЕ в Repository:
// ПЛОХО: бизнес-логика в Repository
@Repository
public class UserRepository {
public void activateUser(Long userId) {
// Это не должно быть здесь!
User user = findById(userId);
user.setActive(true);
user.setActivationDate(now());
user.sendWelcomeEmail(); // Точно не отправлять email!
save(user);
}
}
// ХОРОШО: бизнес-логика в Service
@Service
public class UserService {
private UserRepository userRepository;
private EmailService emailService;
public void activateUser(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new UserNotFoundException(userId));
user.activate(); // бизнес-логика в domain entity
userRepository.save(user);
emailService.sendWelcomeEmail(user.getEmail()); // отправка в service
}
}
2. Валидация НЕ в Repository:
// ПЛОХО
@Repository
public class OrderRepository {
public void save(Order order) {
if (order.getAmount() <= 0) {
throw new InvalidOrderException();
}
if (order.getCustomer() == null) {
throw new InvalidCustomerException();
}
// сохранение...
}
}
// ХОРОШО: валидация через аннотации или в service
@Entity
public class Order {
@NotNull
@DecimalMin("0.01")
private BigDecimal amount;
@NotNull
private Customer customer;
}
3. Трансформация данных НЕ в Repository (только преобразование в domain entity):
// ПЛОХО: создание DTO в Repository
@Repository
public class UserRepository {
public UserDTO findById(Long id) { // Неправильный возвращаемый тип
// ...
}
}
// ХОРОШО: Repository работает с domain сущностями
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findById(Long id); // Работаем с User, не DTO
}
// Трансформацию в DTO делает Service или Controller
@Service
public class UserService {
public UserDTO getUserById(Long id) {
User user = userRepository.findById(id)
.orElseThrow(...);
return UserDTO.from(user); // Трансформация здесь
}
}
Уровень абстракции
Репозиторий — это абстракция над хранилищем данных. Это позволяет:
// Interface скрывает детали реализации
public interface UserRepository {
User save(User user);
Optional<User> findById(Long id);
}
// Можем иметь разные реализации
public class JpaUserRepository implements UserRepository { ... }
public class MongoUserRepository implements UserRepository { ... }
public class FileUserRepository implements UserRepository { ... }
// Service не знает, какая реализация используется
@Service
public class UserService {
private UserRepository userRepository; // Может быть любая реализация
public void createUser(User user) {
userRepository.save(user);
}
}
Область ответственности: краткое резюме
Repository отвечает за:
- ✓ Получение данных из хранилища
- ✓ Сохранение данных в хранилище
- ✓ Удаление данных
- ✓ Поиск по различным критериям
- ✓ Преобразование raw данных в domain объекты
- ✓ Абстрагирование деталей доступа к БД
Repository НЕ отвечает за:
- ✗ Бизнес-логику
- ✗ Валидацию данных (кроме констрейнтов БД)
- ✗ Отправку email, логирование, кеширование
- ✗ Трансформацию в DTO/View модели
- ✗ Обработку исключений на бизнес-уровне
- ✗ Координацию нескольких операций (это service)
Пример правильной архитектуры
// Domain Model
@Entity
public class User {
@Id
private Long id;
private String email;
private String name;
private UserStatus status;
public void activate() {
this.status = UserStatus.ACTIVE;
}
}
// Repository - чистый доступ к данным
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByStatus(UserStatus status);
}
// Service - бизнес-логика
@Service
public class UserService {
private UserRepository userRepository;
private EmailService emailService;
public void activateUser(String email) {
User user = userRepository.findByEmail(email)
.orElseThrow(() -> new UserNotFoundException(email));
user.activate(); // Бизнес-логика
userRepository.save(user); // Сохранение в БД
emailService.sendWelcomeEmail(email); // Бизнес-процесс
}
}
// Controller - обработка HTTP запросов
@RestController
@RequestMapping("/users")
public class UserController {
private UserService userService;
@PostMapping("/{email}/activate")
public ResponseEntity<UserDTO> activateUser(@PathVariable String email) {
userService.activateUser(email);
User user = userService.getUserByEmail(email);
return ResponseEntity.ok(UserDTO.from(user));
}
}
Выводы
Repository в Spring имеет чёткую и ограниченную область ответственности — это работа с данными и только с данными. Это позволяет:
- Писать тестируемый код (mock repository)
- Менять хранилище без изменения business logic
- Поддерживать чистую архитектуру
- Разделять ответственность между слоями
Соблюдение этого принципа критично для поддерживаемого и масштабируемого кода.