서비스 계층 및 트랜잭션


최근에 저는 REAT API를 설계했습니다.

이번에는 역할을 분할하기 위해 서비스 계층을 만들었습니다.

@Service //서비스 선언 (서비스 객체를 스프링부트에 생성)
public class ArticleService {
    @Autowired
    private ArticleRepository articleRepository;

    public List<Article> index() {
        return articleRepository.findAll();
    }

    public Article show(Long id) {
        return articleRepository.findById(id).orElse(null);
    }


    public Article create(ArticleForm dto) {
        Article article = dto.toEntity();
        if(article.getId() != null){
            return null;
        }
        return articleRepository.save(article);
    }

    public Article update(Long id, ArticleForm dto) {
       // 1. 수정용 엔티티 생성
        Article article = dto.toEntity();
       // 2. 대상 엔티티 찾기
        Article target = articleRepository.findById(id).orElse(null);

       // 3. 잘못된 요청 처리
       if(target == null || id != article.getId()){
           return null;
       }
       // 4. 업데이트
        target.patch(article);
        Article updated = articleRepository.save(target);
       return updated;

    }

    public Article delete(Long id) {
        // 대상 찾기
        Article target = articleRepository.findById(id).orElse(null);

        // 잘못된 요청 처리
        if(target == null){
            return null;
        }
        // 대상 삭제
        articleRepository.delete(target);
        return target;

    }

    @Transactional // 해당 메소드를 트랙잭션으로 묶는다!
    public List<Article> createArticle(List<ArticleForm> dtos) {
        // dto 묶음을 entity 묶음으로 변환
        List<Article> articlesList = dtos.stream().map(dto -> dto.toEntity()).collect(Collectors.toList());

        // entity 묶음을 DB로 저장
        articlesList.stream().forEach(article -> articleRepository.save(article));


        // 강제 예외 발생
        articleRepository.findById(-1L).orElseThrow(
                () -> new IllegalArgumentException("결재 실패")
        ) ;

        // 결과값변환

        return null;

    }

@Slf4j
@RestController // RestAPI용 컨트롤러 데이터 JSON을 반환
public class ArticleApiController {

    @Autowired // DI,생성 객체를 가져와 생성
    private ArticleService articleService;

    //GET
    @GetMapping("/api/articles")
    public List<Article> index() {
        return articleService.index();
    }

    @GetMapping("/api/articles/{id}")
    public Article show(@PathVariable Long id) {
        return articleService.show(id);
    }

    //POST
    @PostMapping("/api/articles")
    public ResponseEntity<Article> create(@RequestBody ArticleForm dto) { //JSON 데이터 받기
        Article created = articleService.create(dto);
         return (created != null) ?
                 ResponseEntity.status(HttpStatus.OK).body(created) :
                 ResponseEntity.status(HttpStatus.BAD_REQUEST).build();

    }

    //PATCH
    @PatchMapping("/api/articles/{id}")
    public ResponseEntity<Article>update(@PathVariable Long id, @RequestBody ArticleForm dto) {

      Article updated  =  articleService.update(id,dto);
             return (updated != null) ?
                     ResponseEntity.status(HttpStatus.OK).body(updated):
                     ResponseEntity.status(HttpStatus.BAD_REQUEST).build();

    }




    //delete
    @DeleteMapping("/api/articles/{id}")
    public ResponseEntity<Article> delete(@PathVariable Long id) {
    Article deleted = articleService.delete(id);

        // 데이터 반환
        return (deleted != null)?
                ResponseEntity.status(HttpStatus.NO_CONTENT).build():
                ResponseEntity.status(HttpStatus.BAD_REQUEST).build();

    }

    // 트랙잭션 -> 실패 -> 롤백!
    @PostMapping("/api/transcation-test")
    public ResponseEntity<List<Article>> trascatuonTest(@RequestBody List<ArticleForm> dtos){
       List<Article> createdList = articleService.createArticle(dtos);
        return (createdList != null) ? ResponseEntity.status(HttpStatus.OK).body(createdList):
                ResponseEntity.status(HttpStatus.BAD_REQUEST).build();


    }

}

이전 코드에 비해 코드가 늘어났지만 각 역할이 명확하게 유지되고 있는 것을 확인할 수 있습니다.

———————–

값과 코드로 결과를 쓰고 싶은데 마우스를 집에 두고 나서 쓰기가 힘듭니다.