스프링 컨테이너와 계층화 아키텍쳐 (Layered Architecture)

김찬희·2024년 5월 13일

Spring

목록 보기
5/6
  1. 이전에 개발한 코드를 Controller-Service-Repository로 분리해보기
@RequestMapping("/api/v1/fruit")
@RestController
public class FruitController {

    private final JdbcTemplate jdbcTemplate;

    public FruitController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @PostMapping
    public void addFruit(@RequestBody AddFruitIRequest request) {
        String sql = "INSERT INTO fruit (name, warehousing_date, price) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, request.getName(), request.getWarehousingDate(), request.getPrice());
    }

    @PutMapping
    public void updateFruit(@RequestBody UpdateFruitRequest request) {
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        boolean isFruitNotExist = jdbcTemplate.query(readSql, (rs, rowNum) -> 0, request.getId()).isEmpty();

        if (isFruitNotExist) {
            throw new IllegalArgumentException("과일을 찾을 수 없습니다");
        }

        String sql = "UPDATE fruit SET is_sold = 1 WHERE id = ?";
        jdbcTemplate.update(sql, request.getId());
    }

    @GetMapping("/stat")
    public FruitSalesResponse findByName(@RequestParam String name) {
        String sql = "SELECT SUM(price) FROM fruit WHERE name = ? GROUP BY is_sold";
        List<Long> salesAmounts = jdbcTemplate.query(sql, (rs, rowNum) -> rs.getLong(1), name);

        Long salesAmount = salesAmounts.get(0);
        Long notSaleAmount = salesAmounts.get(1);

        return new FruitSalesResponse(salesAmount, notSaleAmount);
    }
}

Controller

@RequestMapping("/api/v1/fruit")
@RestController
public class FruitController {

    private final FruitService fruitService;

    public FruitController(FruitService fruitService) {
        this.fruitService = fruitService;
    }

    @PostMapping
    public void addFruit(@RequestBody AddFruitRequest request) {
        fruitService.addFruit(request);
    }

    @PutMapping
    public void updateFruit(@RequestBody UpdateFruitRequest request) {
        fruitService.updateFruit(request);
    }

    @GetMapping("/stat")
    public FruitSalesResponse findByName(@RequestParam String name) {
        return fruitService.findByName(name);
    }
}

Service

@Service
public class FruitService {

    private final FruitRepository fruitRepository;

    public FruitService(FruitRepository fruitRepository) {
        this.fruitRepository = fruitRepository;
    }

    public void addFruit(AddFruitRequest request) {
        fruitRepository.addFruit(request.getName(), request.getWarehousingDate(), request.getPrice());
    }

    public void updateFruit(UpdateFruitRequest request) {
        if (fruitRepository.isFruitNotExist(request.getId())) {
            throw new IllegalArgumentException("과일을 찾을 수 없습니다");
        }

        fruitRepository.updateFruit(request.getId());
    }

    public FruitSalesResponse findByName(String name) {
        List<Long> salesAmounts = fruitRepository.findByName(name);

        Long salesAmount = salesAmounts.get(0);
        Long notSaleAmount = salesAmounts.get(1);

        return new FruitSalesResponse(salesAmount, notSaleAmount);
    }
}

Repository

@Repository
public class FruitRepository {

    private final JdbcTemplate jdbcTemplate;

    public FruitRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void addFruit(String name, LocalDate warehousingDate, long price) {
        String sql = "INSERT INTO fruit (name, warehousing_date, price) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, name, warehousingDate, price);
    }

    @Override
    public List<Long> findByName(String name) {
        String sql = "SELECT SUM(price) FROM fruit WHERE name = ? GROUP BY is_sold";
        return jdbcTemplate.query(sql, (rs, rowNum) -> rs.getLong(1), name);
    }

    @Override
    public boolean isFruitNotExist(long id) {
        String readSql = "SELECT * FROM fruit WHERE id = ?";
        return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
    }

    @Override
    public void updateFruit(long id) {
        String sql = "UPDATE fruit SET is_sold = 1 WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }
}
  1. Repository를 FruitMemoryRepository, FruitMySQLRepository로 나누고 이를 @Primary 어노테이션을 사용해 Repository 변경하기

<"interface">FruitRepository

public interface FruitRepository {

    void addFruit(String name, LocalDate warehousingDate, long price);

    List<Long> findByName(String name);

    void updateFruit(long id);

    boolean isFruitNotExist(long id);
}
@Primary
@Repository
public class FruitMySqlRepository implements FruitRepository {
}

@Repository
public class FruitMemoryRepository implements FruitRepository {
}

@Primary 를 가지고 있는 구현체인 FruitMySqlRepository가 interface FruitRepository를 구현하게 된다.

0개의 댓글