Tags

πŸ§‘β€πŸ’» ν•¨μˆ˜ν˜•μœΌλ‘œ νŠΈλžœμž­μ…˜ μ²˜λ¦¬ν•˜κΈ°

namjug-kim β€’ 2019λ…„ 2μ›” 18일
functional programming

μ†Œκ°œ

μŠ€ν”„λ§μ˜ AOPλ₯Ό 톡해 νŠΈλžœμž­μ…˜(@Transactional)을 닀루닀 보면 같은 μž‘μ—…μ„ μ§„ν–‰ν•˜λ©΄μ„œ 이전 νŠΈλžœμž­μ…˜ μƒμ†λ°›κ±°λ‚˜(REQUIRE), μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ—΄μ–΄μ„œ μ²˜λ¦¬ν•΄μ•Όν•˜λŠ”(REQUIRE_NEW)일이 λ°œμƒν•˜κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

ν˜ΈμΆœμ§€μ μ—μ„œ νŠΈλžœμž­μ…˜μ„ μ„ νƒμ μœΌλ‘œ μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•˜κΈ° μœ„ν•œ νŠΈλžœμž­μ…˜ 처리λ₯Ό μœ„ν•œ monadic container type 을 κ΅¬ν˜„ν•΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€.

AOPλ₯Ό μ΄μš©ν•œ νŠΈλžœμž­μ…˜

@Transactional
public UserEntity saveUser(String userName) {
   UserEntity userEntity = new UserEntity();
   userEntity.setUserName(userName);
   return userRepository.save(userEntity);
}
public void run() {
   saveUser("testUserName");
}

@Transactional μ–΄λ…Έν…Œμ΄μ…˜μ„ μ‚¬μš©ν•˜μ—¬ νŠΈλžœμž­μ…˜ 처리λ₯Ό ν•˜κ³  싢은 클래슀, ν˜Ήμ€ λ©” μ„œλ“œμ—μ„œ νŠΈλžœμž­μ…˜μ„ μ‚¬μš© ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

이λ₯Όν†΅ν•΄μ„œ 쉽고 가독성 쒋은 μ½”λ“œλ₯Ό 지 수 μžˆμ—ˆμ§€λ§Œ, μ„±λŠ₯ νŠœλ‹μ΄λ‚˜ μƒˆλ‘œμš΄ κΈ°λŠ₯ μΆ”κ°€ λ“±μ˜ μƒν™©μ—μ„œ 같은 μ„œλΉ„μŠ€μ—μ„œ DB νŠΈλžœμž­μ…˜μ„ 짧게 κ°€μ Έκ°€κΈ° μœ„ν•΄μ„œ λΆ„λ¦¬ν•˜κ±°λ‚˜ λ‹€λ₯Έλ°©μ‹μ˜ failoverλ₯Ό μˆ˜ν–‰ν•˜κΈ° μœ„ν•΄μ„œ μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ—΄μ–΄μ•Όν•˜λŠ” μš”κ΅¬μ‚¬ν•­μ΄ μƒκΈ°κ²Œ λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ΄λ•Œ @Transactional(propagation = Propagation.REQUIRES_NEW)λ₯Ό μΆ”κ°€ν•œ μƒˆλ‘œμš΄ λ©”μ†Œλ“œλ₯Ό μΆ”κ°€ ν•˜λŠ”κ²ƒκ³Ό 같이 AOPλ§ŒμœΌλ‘œλŠ” μ΄λŸ¬ν•œ μš”κ΅¬μ‚¬ν•­μ„ μˆ˜ν–‰ν•˜λŠ”λ° λ§Žμ€ 어렀움이 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

ν•¨μˆ˜ν˜• μ ‘κ·Ό

ν•¨μˆ˜ν˜• νŠΈλžœμž­μ…˜ μ‚¬μš©ν•˜κΈ°


// AOP
/*@Transactional
public UserEntity saveUser(String userName) {
   UserEntity userEntity = new UserEntity();
   userEntity.setUserName(userName);
   return userRepository.save(userEntity);
}*/

public Transaction<UserEntity> saveUser(String userName) {
      return Transaction.create(() -> {
          UserEntity userEntity = new UserEntity();
          userEntity.setUserName(userName);
          return userRepository.save(userEntity);
      })
}
public void run() {
   // saveUser("testUserName"); AOP
   saveUser("testUserName")
			. execute(transactionManager, Propagation.REQUIRE); // νŠΈλžœμž­μ…˜ λ§€λ‹ˆμ €μ™€ Propagation μ„€μ •
}

Transaction<T>κ°€ ν˜ΈμΆœλ˜λŠ” λΆ€λΆ„ execute μ—μ„œ νŠΈλžœμž­μ…˜ λ§€λ‹ˆμ €μ™€ Propagation을 μ§€μ •ν•΄μ€ŒμœΌλ‘œμ¨ νŠΈλžœμž­μ…˜μ„ 쒀더 자유둭게 λ‹€λ£° 수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

νŠΈλžœμž­μ…˜ Monadic Container ꡬ쑰

public class Transaction<T> implements TransactionPublisher {
    private final Function<Void, T> action;

    private Transaction(Function<Void, T> action) {
        this.action = action;
    }

    public static <T> Transaction<T> create(Callable<T> callable) {
        return new Transaction<>(aVoid -> silentCallable(callable));
    }

    public static Transaction<Void> create(Runnable runnable) {
        return new Transaction<>(aVoid -> {
            runnable.run();
            return null;
        });
    }

    public <U> Transaction<U> map(Function<? super T, ? extends U> mapper) {
        return new Transaction<>(action.andThen(mapper));
    }

    public <U> Transaction<U> flatMap(Function<? super T, ? extends Transaction<U>> mapper) {
        return new Transaction<>(aVoid -> {
            Transaction<U> apply = action.andThen(mapper).apply(null);
            return apply.get();
        });
    }

    private T get() {
        return action.apply(null);
    }

    @Override
    public T execute(PlatformTransactionManager platformTransactionManager, Propagation propagation) {
        TransactionTemplate txTemplate = new TransactionTemplate(platformTransactionManager);

        txTemplate.setPropagationBehavior(propagation.value());
        return txTemplate.execute(status -> action.apply(null));
    }

    @SneakyThrows
    private static <T> T silentCallable(Callable<T> callable) {
        return callable.call();
    }
}
public interface TransactionPublisher<T> {
    T execute(PlatformTransactionManager platformTransactionManager, Propagation propagation);
}

νŠΈλžœμž­μ…˜ λ‚΄λΆ€μ—μ„œ 싀행될 μ½”λ“œλ₯Ό Function<Void, T> action 으둜 가지고 있고 μ‹€μ œ μ½”λ“œκ°€ μ‹€ν–‰λ˜λŠ” μ‹œμ μΈ Transaction#execute μ—μ„œ νŠΈλžœμž­μ…˜ λ§€λ‹ˆμ €μ™€ Propagation을 μ£Όμž…λ°›λ„λ‘ ν•˜μ˜€μŠ΅λ‹ˆλ‹€.

flatMap μ‚¬μš©ν•˜κΈ°

public Transaction<UserEntity> saveUser(String userName) {
      return Transaction.create(() -> {
          UserEntity userEntity = new UserEntity();
          userEntity.setUserName(userName);
          return userRepository.save(userEntity);
      })
}
public void run() {
   // saveUser("testUserName"); AOP
   saveUser("testUserName")
			.flatMap(u -> saveUser("testUserName"))
			. execute(transactionManager, Propagation.REQUIRE); // νŠΈλžœμž­μ…˜ λ§€λ‹ˆμ €μ™€ Propagation μ„€μ •
}

flatMap을 μ΄μš©ν•˜μ—¬ Transaction<T> λ₯Ό μ—°κ²°ν•¨μœΌλ‘œμ¨ 같은 νŠΈλžœμž­μ…˜μ—μ„œ μ‹€ν–‰ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ’…ν•©

νŠΈλžœμž­μ…˜μ²˜λ¦¬λ₯Ό μœ„ν•œ monadic containerλ₯Ό λ§Œλ“€μ–΄ λ³΄μ•˜μŠ΅λ‹ˆλ‹€. 이λ₯Ό ν†΅ν•΄μ„œ ν˜ΈμΆœλ˜λŠ” μ‹œμ μ— νŠΈλžœμž­μ…˜μ„ μƒμ†λ°›κ±°λ‚˜, μƒˆλ‘œμš΄ νŠΈλžœμž­μ…˜μ„ μ˜€ν”ˆν•˜λŠ”κ²ƒμ„ μ„ νƒμ μœΌλ‘œ μ‚¬μš© ν•  수 있게 λ˜μ—ˆμŠ΅λ‹ˆλ‹€.

μ—¬μ „νžˆ κΈ°λ³Έμ μœΌλ‘œλŠ” 쒀더 μ΅μˆ™ν•œ @Transactional을 μ΄μš©ν•œ νŠΈλžœμž­μ…˜ 처리λ₯Ό 기본으둜 μ‚¬μš©ν•˜κ³  μžˆμ§€λ§Œ ν•„μš”ν•œ μž₯μ†Œμ— Transaction<T> 을 μ΄μš©ν•œ ν•¨μˆ˜ν˜• νŠΈλžœμž­μ…˜μ„ μ‚¬μš©ν•¨μœΌλ‘œμ¨ λͺ…μ‹œμ μœΌλ‘œ ν‘œν˜„ν•  ν•„μš”κ°€ μžˆλŠ”κ³³μ„ 쒀더 κΉ”λ”ν•˜κ²Œ 처리 ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€.

μœ„ μ†ŒμŠ€μ½”λ“œλŠ” GitHub - namjug-kim/functional-transaction λ₯Ό ν†΅ν•΄μ„œ 보싀 수 μžˆμŠ΅λ‹ˆλ‹€.