14. Сделать рефакторинг сервиса пользователей
Условие задачи:
📌 Дан сервис UserService. Нужно провести рефакторинг кода: исправить проблемы с зависимостями, транзакциями, стилем и потенциальными ошибками в обработке пользователей.
Код:
class UserService {
private UserRepository repo = new UserRepository();
private RegionService regionService;
public UserService(final ApplicationContext appCtx) {
regionService = appCtx.getBean("regionService", RegionService.class);
}
public void processNewUsers(final List<User> users, String regionName) {
...
users = createUsers(users);
...
users.stream()
.foreach(u -> regionService.updateRegionLink(u.getId(), regionName));
}
@Transactional
public List<User> createUsers(final List<User> users) {
return users.stream()
.map(u -> repo.saveUser(u))
.collect(Collectors.toList());
}
private User getUser(final int userId) {
return repo.getUserById(userId);
}
}
Спойлеры к решению
Подсказки
💡
💡
💡
💡
💡 Лучше сделать зависимости
💡 Стоит подумать, какой метод должен быть транзакционной точкой входа:
new UserRepository() и appCtx.getBean(...) в сервисе — плохая практика, лучше обычный DI.💡
users = createUsers(users) не скомпилируется, если параметр объявлен как final.💡
foreach нужно писать как forEach.💡
@Transactional на методе, который вызывается внутри того же класса, может не сработать через Spring proxy.💡 Лучше сделать зависимости
final и передавать их через конструктор.💡 Стоит подумать, какой метод должен быть транзакционной точкой входа:
createUsers() или processNewUsers().Решение
@Service
@RequiredArgsConstructor
class UserService {
private final UserRepository repo;
private final RegionService regionService;
@Transactional
public void processNewUsers(final List<User> users, final String regionName) {
List<User> savedUsers = createUsers(users);
savedUsers.forEach(u -> regionService.updateRegionLink(u.getId(), regionName));
}
private List<User> createUsers(final List<User> users) {
return users.stream()
.map(repo::saveUser)
.collect(Collectors.toList());
}
public User getUser(final int userId) {
return repo.getUserById(userId);
}
}
В исходном коде основные проблемы: ручное создание зависимостей вместо DI, использование
ApplicationContextв сервисе, ошибка сfinal users, опечаткаforeachи риск, что@Transactionalне сработает при внутреннем вызове.
Лучше сделатьprocessNewUsers()транзакционной точкой входа, а зависимости внедрять через конструктор.