public void upgradeLevels() throws SQLException {
// 트랜잭션 경계 설정
  TransactionStatus status = this.transactionManager.getTransaction(
      new DefaultTransactionDefinition());

  try {
  // 비즈니스 로직
    List<User> users = userDao.getAll();
    for (User user : users) {
      if (policy.canUpgradeLevel(user)) {
        upgradeLevel(user);
      }
    }
    
// 트랜잭션 경계 설정
    this.transactionManager.commit(status);
  } catch (RuntimeException e) {
    this.transactionManager.rollback(status);
    throw e;
  }
}

⇒ 비즈니스 로직과 트랜잭션 경계 설정 코드는 완벽하게 독립적인 코드

비즈니스 로직과 트랜잭션 경게 설정의 분리

public void upgradeLevels() throws SQLException {
    //트랜잭션 경계 설정
    TransactionStatus status = this.transactionManager.getTransaction(
        new DefaultTransactionDefinition());

    try {
      upgradeLevelsInternal();
      //트랜잭션 경계 설정
      this.transactionManager.commit(status);
    } catch (RuntimeException e) {
      this.transactionManager.rollback(status);
      throw e;
    }
  }
  
  private void upgradeLevelsInternal(){
    // 비즈니스 로직
    List<User> users = userDao.getAll();
    for (User user : users) {
      if (policy.canUpgradeLevel(user)) {
        upgradeLevel(user);
      }
    }
  }

변경점

리팩토링 후 테스트! 아 맞다 테스트! 아 맞다 테스트! 아 맞다 테스트! 아 맞다 테스트!