5. 서비스추상화

5 서비스 추상화

서비스 로직에 대한 처리 간단한 예제가 역시 등장.. 책 참고

5.1 사용자 레벨 관리 기능추가

DB에는 숫자로, 코드상에서는 Enum을 활용하여 구현 여기서 말하는 주된 내용은 테스트를 제대로 작성해두면(DB연동 테스트코드 까지) 기능추가가 되서 오타가 있더라도 빠르게 발견하여 수정할 수 있다는 것. 대충 작성한 테스트코드는 오히려 버그를 만드므로 테스트 작성시 꼼꼼히 작성할 필요가 있음

  • 서비스 클래스 추가

    데이터베이스 연결 부분과, 서비스 구현로직을 따로 분리하여 서로 결합력을 낮춤.

코드개선

  • 코드중복여부, 중복성

  • 무엇을 하는 것인지 이해하기 편한가? 가독성

  • 자신이 있어야할 자리에 있는가?, 단일책임원칙

  • 변경이 일어난다면 어떤 것이 있을 수 있꼬, 쉽게 대응이 가능한가?, 개방폐쇄원칙, OCP

  • Level 이늄

    ```java public enum Level { BASIC(1){ @Override boolean isUpgrade(User user) { return user.getLevel() == this && user.getLoginCount() >= 50; } }, SILVER(2){ @Override boolean isUpgrade(User user) { return user.getLevel() == this && user.getRecommendCount() >= 30; } }, GOLD(3){ @Override boolean isUpgrade(User user) { return false; } } ; private final int value;

    Level(int value) { this.value = value; }

    public int getLevelIntValue() { return this.value; }

    public static Level valueOf(int value) { switch (value) { case 1: return BASIC; case 2: return SILVER; case 3: return GOLD; default: throw new AssertionError("Unknown value : " + value); } }

    public static Level nextLevel(Level level){ return valueOf(level.getLevelIntValue() + 1); }

    abstract boolean isUpgrade(User user); // 추가한 부분, 변경에 대한 빠른 대처를 위함 }

- UserService.upgradeLevels
FP 기반으로 작성
1. upgrade 조건식을 -> Level 이늄의 추상메서드로 변경
2. 따라서 다형성이 적용되어, upgradeLevels 메서드에서 코드수정이 필요없음
3. ex) 플래티넘 등급이 생겼을 때, `Level 이늄`에서 등급에 맞게 추가작업만 하면 끝! (upgrade 조건, 레벨링숫자)
```java
  public void upgradeLevels() {
    List<User> users = dao.getAll();
    users.stream().filter(user -> user.getLevel().isUpgrade(user))
                  .forEach(user -> {
                    user.setLevel(Level.nextLevel(user.getLevel()));
                    dao.update(user);
                  });
  }

5.2 트랜잭션

최소의 작업단위. 원자성 확보. 즉 데이터가 기능처리에서 성공과 실패시에 원자성을 확보해야 함. 실패하면 이전상태로 데이터가 그대로 있어야 한다가 핵심. 성공하면 반영되어야한다가 핵심 트랜잭션 롤백 말그대로 실패시 데이터가 이전상태로 롤백 되어야함 트랜잭션 커밋 말 그대로 성공시 데이터가 전부 제대로 반영 되어야 함

경계 지점

트랙잰션은 시작지점과 끝지점이 있고, 이 지점 사이를 하나의 작업단위로 보게 됨. 2가지 적용 1. 커밋 2. 롤백

JDBC 트랙잭션 -> Connection 객체로 결정됨

// 트랙잰션 시작
connection.setAutoCommit(false);
// .. 작업
// 트랙잭션 끝
connection.commit(); // or connection.rollback();

JdbcTemplate 같은 경우는 메서드 내부적으로 connection과 실행이 내포되어 있다. 따라서 메서드 단위가 하나의 트랜잭션으로 인식이 되며, 보통 커넥션의 범위가 트랜잭션의 범위보다 넓다.

트랜잭션 동기화

트랜잭션의 단위에서 롤백 or 커밋이 이뤄질때의 원자성 확보를 말하는것 같다. 여기서 나오는 내용중 살펴봐야할 건.

동기화적용을 위해 분리된 책임이 깨진다는점. (파라미터 구현하여 전달하는 행위) 이것은 부수효과를 만들기 때문에 멀티쓰레드 환경에서 논세이프, 즉 안전하지 않게 된다. 그러므로 그 선언위치는 항상 메서드 단위로 이루어져야하고, 객체의 생성 및 종료는 그 함수내에서 관리 되어야 한다.

DataSourceUtils.getConnection(dataSource) -> 동기화 관리 생성 및 동기화 저장소 제공을 해줌

트랜잭션 서비스 추상화

JDBC의 Connection 경우 한 개 이상의 DB 작업을 한개의 Connection으로 처리 불가능. 따라서 서비스 추상화가 필요함 즉, DB의 Connection을 통해서가 아니라 별도의 TransactionManager를 통해 트랜잭션을 관리하는 것이다. 이것을 글로벌 트랜잭션이라고 한다. JTA 같은것이 글로벌 트랜잭션을 지원하는데 트랜잭션은 이넘이 관리 할 수 있도록 위임한다.

JTA를 활용하면 분산트랜잭션이 가능. 즉, 하나 이상의 DB가 참여하는 트랜잭션을 만들려면 JTA를 사용 하이버네이트 -> Connection 대신 Session을 사용

스프링의 트랜잭션 서비스 추상화

트랜잭션 경계설정

PlatformTransaction 인터페이스

로컬트랜잭션 이용

DataSourceTransactionManager구현체 사용

// 트랜잭션의 속성 전달
TransactionStatus status = manager.getTransaction(new DefaultTransactionDefinition());

// status 트랜잭션의 조작이 필요한 경우 파라미터로 전달 (ex commit, rollbak...)

스프링의 트랜잭션 추상화 -> 트랜잭션 동기화를 사용함

// DI로 구성하는게 낫겠지?
transactionManager = new DataSourceTransactionManager(dataSource); // 기본 Jdbc 이용한 단일 DB
transactionManager = new JtaTransactionManager();   // JDNI 사용, DataSource도 서버꺼 사용해야함
transactionManager = new JpaTransactionManager();   // JPA 이런식으로 TM을 구현체로 설정할 수 있다.

5.3 서비스 추상화와 단일 책임 원칙

단일책임원칙 : 한 모듈은 한가지 기능, 하나의 모듈이 바뀌는 이유는 1가지여야 한다 이 또한 책임의 분리, OCP를 잘 만족해야겠지

5.4 메일서비스 추상화

비중이 높지는 않을

사용시 아래 추가

<dependency>
  <groupId>javax.mail</groupId>
  <artifactId>mail</artifactId>
  <version>1.4.7</version>
</dependency>

JavaMail

  • Session

  • MimeMessage

Spring 에서 메일

  • JavaMailSenderImpl (인터페이스 : MailServer)

    • host : 메일서버 지정

  • SimpleMailMessage

서비스 추상화란 트랜잭션과 같이 기능은 유사하나 사용방법이 다른 로우레벨의 다양한 기술에 대해 추상인터페이스일관성 있는 접근 방법을 제공해주는 것

Mail 서버에도 트랜잭션 개념이 있어야 한다. 발송실패 했을 때 롤백개념!

스텁, 의존오브젝트 목오브젝트의 등장

@Autowired 의 문제점

주입할때 선언된 타입이 인터페이스 or 추상클래스 처럼 상위 개념일 때 문제 발생.. @Autowired타입으로 주입을 한다. 그런데 A라는 인터페이스를 구현하는 구현체 B,C,D 이렇게 여러가지가 되면 어떤 타입을 주입해야할지 애매하게되므로.. 예외가 발생한다. 그래서 이런ㅈ

Last updated