← Назад к вопросам

Какая область ответственности у Repository в Spring?

1.3 Junior🔥 271 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI28 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Область ответственности 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
  • Поддерживать чистую архитектуру
  • Разделять ответственность между слоями

Соблюдение этого принципа критично для поддерживаемого и масштабируемого кода.

Какая область ответственности у Repository в Spring? | PrepBro