프레젠테이션 계층
서비스 계층
데이터 접근 계층
서비스 계층은 특정 기술에 종속적이지 않은 순수한 자바 코드로 작성해 최대한 변경없이 유지되어야 한다.
public void accountTransfer(String fromId,String toId,int money) throws SQLException {
Member fromMember = memberRepositoryV1.findById(fromId);
Member toMember = memberRepositoryV1.findById(toId);
memberRepositoryV1.update(fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepositoryV1.update(toId, toMember.getMoney() + money);
}
위의 트랜잭션이 없는 서비스 로직은 대부분 순수한 자바 코드로 짜여져 있지만 SQLException
이 JDBC 기술에 종속적인 에러이다. 만약 JPA 기술로 변경한다면 이 코드를 수정해야 할 것이다.
public void accountTransfer(String fromId,String toId,int money) throws SQLException {
Connection con = dataSource.getConnection();
try {
con.setAutoCommit(false);
Member fromMember = memberRepository.findById(con, fromId);
Member toMember = memberRepository.findById(con, toId);
memberRepository.update(con, fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepository.update(con, toId, toMember.getMoney() + money);
con.commit();
} catch (Exception e) {
con.rollback();
throw new IllegalStateException(e);
}finally {
if (con != null) {
try {
con.setAutoCommit(true);
} catch (Exception e) {
log.info("error", e);
}finally {
JdbcUtils.closeConnection(con);
}
}
}
}
위 코드는 트랜잭션을 적용한 서비스 로직이다. 전의 코드보다 DataSource
, Connection
과 같이 JDBC 종속적인 기술을 더 많이 사용하고 있다.
또한 핵심 로직보다 트랜잭션을 사용하는 코드가 더 많고 비즈니스 로직과 JDBC 기술이 섞여 있어 유지보수하기 힘들다.
지금처럼 서비스와 레파지토리가 모두 JDBC 기술에 의존하고 있다면 단일 책임 원칙을 위반하는 것이다.
(변경 포인트는 하나여야 한다.)
스프링은 이런 문제를 해결하기 위해 트랜잭션 추상화를 위한 PlaformTransactionManager
트랜잭션 매니저 인터페이스와 여러 구현체들을 제공한다.
동작방식
1. 트랜잭션을 시작하려면 트랜잭션 매니저는 데이터소스를 통해 커넥션을 만들고 트랜잭션을 지삭한다.
2. 트랜잭션 매니저는 트랜잭션이 시작된 커넥션을 동기화 매니저에 보관한다.
3. 리포지토리는 트랜잭션 동기화 매니저에 보관된 커넥션을 꺼내서 사용한다.
4. 트랜잭션이 종료되면 트랜잭션이 매니저는 트랜잭션 동기화 매니저을 통해 트랜잭션을 종료하고 커넥션도 닫는다.
DataSourceUtils.getConnection()
DataSourceUtils.releaseConnection()
Repository가 트랜잭션 유지할 지를 판단하는 방법은 트랜잭션이 트랜잭션 동기화 매니저에 의해 관리되고 있는지로 결정한다.
JDBC 종속성이 사라진 서비스 계층
public void accountTransfer(String fromId,String toId,int money) {
TransactionStatus status = transactionManager
.getTransaction(new DefaultTransactionDefinition());
try {
Member fromMember = memberRepository.findById(fromId);
Member toMember = memberRepository.findById(toId);
memberRepository.update(fromId, fromMember.getMoney() - money);
validation(toMember);
memberRepository.update(toId, toMember.getMoney() + money);
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new IllegalStateException(e);
}
}
PlatformTransactionManager
의 commit(), rollback() 이 수행되면 트랜잭션이 release 된다.
PlatformTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
JDBC의 DataSource와 동작하는 트랜잭션 매니저 구현체는 DataSourceTransactionManager
이다.
PlatformTransactionManager
에 의해 서비스 계층은 특정관련 기술에 의존하지 않게 되었다.