Task Livecoding Java Order Service Review2

71. Code Review: OrderService #

πŸ”₯ Π‘Π΄Π΅Π»Π°ΠΉ Ρ€Π΅Π²ΡŒΡŽ ΠΊΠΎΠ΄Π° Π½ΠΈΠΆΠ΅. На Ρ‡Ρ‚ΠΎ ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒ Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅, Ρ‡Ρ‚ΠΎ ΠΈΡΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ, ΠΊΠ°ΠΊΠΈΠ΅ риски ΠΈ ΡƒΠ»ΡƒΡ‡ΡˆΠ΅Π½ΠΈΡ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹?

public class OrderService {

@Autowired
private OrderRepository orderRepository;

@Inject
private AuditRepository auditRepository;

public void save(Order order) throws OrderValidationException {
    try {
        saveOrderInternal(order);
    } catch (Throwable e) {
        e.printStackTrace();
        throw new OrderSaveException(e, order);
    }
}

@Transactional
private void saveOrderInternal(Order order) throws OrderValidationException {
    auditRepository.save(order);
    if (order.getClient().getName() == """") {
            throw new OrderValidationException(""ПолС ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π½Π΅ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΎ."");
        }
        orderRepository.save(order);
    }
}

public class OrderValidationException extends Exception {
    // ...
}

Π‘ΠΏΠΎΠΉΠ»Π΅Ρ€Ρ‹ ΠΊ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡŽ
Подсказки
πŸ’‘ НС смСшивай Ρ€Π°Π·Π½Ρ‹Π΅ DI-Π°Π½Π½ΠΎΡ‚Π°Ρ†ΠΈΠΈ: Π²Ρ‹Π±Π΅Ρ€ΠΈ @Autowired ΠΈΠ»ΠΈ @Inject.
πŸ’‘ НС ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉ Throwable Π² catch: Π»ΡƒΡ‡ΡˆΠ΅ Exception ΠΈΠ»ΠΈ Π±ΠΎΠ»Π΅Π΅ ΡƒΠ·ΠΊΠΈΠΉ Ρ‚ΠΈΠΏ.
πŸ’‘ Π’Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΡŽ Π»ΡƒΡ‡ΡˆΠ΅ Π΄Π΅Π»Π°Ρ‚ΡŒ Π΄ΠΎ сохранСния, Π° Π½Π΅ посрСди Ρ‚Ρ€Π°Π½Π·Π°ΠΊΡ†ΠΈΠΈ.
πŸ’‘ Для строк ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉ isEmpty() ΠΈΠ»ΠΈ StringUtils.isBlank(), Π° Π½Π΅ == "".
πŸ’‘ ΠœΠ΅Ρ‚ΠΎΠ΄ saveOrderInternal ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ, Π½ΠΎ аннотация @Transactional Π½Π΅ подСйствуСт Π½Π° Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠ΅ Π²Ρ‹Π·ΠΎΠ²Ρ‹.
πŸ’‘ auditRepository.save(order) Π΄ΠΎ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ β€” ΠΌΠΎΠΆΠ΅Ρ‚ ΠΎΡΡ‚Π°Π²ΠΈΡ‚ΡŒ β€œΠ³Ρ€ΡΠ·Π½Ρ‹Π΅β€ записи.
πŸ’‘ Π›ΡƒΡ‡ΡˆΠ΅ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с кастомными ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡΠΌΠΈ: checked vs unchecked. ΠžΠ±Ρ‹Ρ‡Π½ΠΎ для бизнСс-ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠΉ Π΄Π΅Π»Π°ΡŽΡ‚ RuntimeException.
πŸ’‘ Π›ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ Ρ‡Π΅Ρ€Π΅Π· logger.error(...), Π° Π½Π΅ printStackTrace().
πŸ’‘ ΠΠ°Ρ€ΡƒΡˆΠ΅Π½ΠΈΠ΅ SRP: класс ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎ ΠΎΡ‚Π²Π΅Ρ‡Π°Π΅Ρ‚ Π·Π° Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΡŽ, сохранСниС ΠΈ Π°ΡƒΠ΄ΠΈΡ‚.
РСшСниС
@Service
public class OrderService {

    private final OrderRepository orderRepository;
    private final AuditRepository auditRepository;
    private final OrderValidator orderValidator;
    private static final Logger log = LoggerFactory.getLogger(OrderService.class);

    @Autowired
    public OrderService(OrderRepository orderRepository,
                        AuditRepository auditRepository,
                        OrderValidator orderValidator) {
        this.orderRepository = orderRepository;
        this.auditRepository = auditRepository;
        this.orderValidator = orderValidator;
    }

    @Transactional
    public void save(Order order) {
        try {
            orderValidator.validate(order); // валидация Π΄ΠΎ сохранСния

            orderRepository.save(order);
            auditRepository.save(order);

        } catch (OrderValidationException e) {
            log.warn("Ошибка Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ Π·Π°ΠΊΠ°Π·Π°: {}", order, e);
            throw e;
        } catch (Exception e) {
            log.error("Ошибка сохранСния Π·Π°ΠΊΠ°Π·Π°: {}", order, e);
            throw new OrderSaveException(e, order);
        }
    }
}

@Component
public class OrderValidator {
    public void validate(Order order) throws OrderValidationException {
        if (order.getClient() == null || 
            order.getClient().getName() == null || 
            order.getClient().getName().isBlank()) {
            throw new OrderValidationException("ПолС ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Π½Π΅ Π·Π°ΠΏΠΎΠ»Π½Π΅Π½ΠΎ.");
        }
    }
}
  • Валидация вынСсСна Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ класс.

  • Π˜ΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°ΡŽΡ‚ΡΡ с Π»ΠΎΠ³ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ.

  • @Transactional примСняСтся Π½Π° ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄, Ρ‡Ρ‚ΠΎΠ±Ρ‹ AOP-прокси сработал.

  • auditRepository.save(order) выполняСтся послС ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠ³ΠΎ сохранСния Π·Π°ΠΊΠ°Π·Π°.