본문 바로가기
Spring

Spring 비지니스 로직 위치

by 옹알이옹 2024. 2. 25.
목차

1. 서버스 계층에서의 구현 장/단점

2. 도메인 객체에서의 구현 장/단점

3. 결론


 

 1. 서비스 계층에서의 구현 장/단점

 

우선 서비스 계층에서 구현은 아래와 같다.

  • 하나의 Customer 클래스가 존재하고 Getter, Setter 이외의 로직은 존재하지 않는다.
  • 서비스 계층에서 해당 객체를 통해 필요한 값을 가져와 필요에 따라 함수를 작성한다.
  • 고객의 등급은 파라미터로 받은 값에 따라 분기 처리하여 사용한다.
@Service
public class CustomerService {

	// 이자 금액을 계산하는 함수
    public double getInterest(int cost,String grade){
        Customer customer = new Customer();
        customer.setId("C1");
        customer.setBalance(100);

        if (grade.equals("VIP")) {
            return customer.getBalance() * 0.2;
        } else {
            return customer.getBalance() * 0.1;
        }
    }
}
@Getter
@Setter
public class Customer {
    private String id;
    private double balance;
}

 

장점

 

현재 상황에서 추가로 고객의 이자를 계산한 뒤 그만큼 잔액을 증가시키는 함수가 추가된다고 가정 한다.

-> 서비스 계층에서 해당 함수를 추가만 하면 다른 곳에서의 수정은 필요 없다.

-> 함수에 대한 추가가 용이하다.

 

단점

 

추가적인 함수가 필요한 게 아닌 고객의 유형이 추가된 상황이 발생하였다.

-> 고객이 추가될 때마다 서비스 계층의 if else문을 수정해야 하는 상황이 발생한다.

-> 유형에 대한 추가(구조)가 어렵다

-> 해당 상황은 기본적으로 개방 폐쇄 원칙을 위배한다. 고객 타입 추가라는 행위가 필연적으로 수정을 야기한다.

 

 

 2. 도메인 객체에서의 구현 장/단점

 

도메인 객체에서 구현은 아래와 같다.

  • 하나의 Customer 인터페이스가 존재하고 그에 대한 구현체가 여러 개 존재한다.
  • 인터페이스에서 이자율 계산이라는 행위를 정의한 뒤 각 구현체에서 등급에 따른 행위를 실제 구현한다.
public interface Customer {
    public double getInterest();
}
public class VipCustomer implements Customer{

    private String id;
    private double balance;
    @Override
    public double getInterest() {
        return balance * 0.2;
    }
}
public class NormalCustomer implements Customer{

    private String id;
    private double balance;
    @Override
    public double getInterest() {
        return balance * 0.1;
    }
}
실제 해당 인터페이스를 사용하는 클라이언트 부분(서비스 계층) 까지 표현 하면 이야기가 길어지므로 생략한다.

 

 

장점

 

추가적인 함수가 필요한 게 아닌 고객의 유형이 추가된 상황이 발생하였다.

->  고객 유형이 추가되어도 해당 객체를 사용하는 곳에서는 수정이 필요하지 않다.

->  유형에 대한 추가(구현체, 구조)가 용이하다.

->  개방 폐쇄 원칙을 준수하는 코드를 짤 수 있다.(객체지향적인 코드)

 

단점

 

추가로 고객의 이자를 계산한 뒤 그만큼 잔액을 증가시키는 함수가 추가된다고 가정한다.

-> 하나의 Customer 구현체를 추가로 생성 한 뒤 getInterest 함수를 오버라이딩 해주어야 한다.

-> 함수를 추가하므로 인터페이스와 그의 구현체 모두 수정이 필요해진다.

-> 함수 추가가 불편하다.


 3. 결론

 

그래서 어떤 방식이 더 좋은 것이냐?

 

결론부터 말하면 해당 질문에는 답이 없다.

 

한 동안 DDD와 객체 지향에 대해 공부하며, 나는 무조건적으로 도메인 객체에 로직을 작성하는 DDD 가 더 바람직한 
구조라고 생각하고 있었다.

 

그런데 며칠 전 비지니스 로직의 바람직한 위치를 묻는 한 포스팅을 보았고, 답변자는 그냥 케바케라고만 심플하게 답변을 한 것을 보았다.

DDD가 무조건적으로 바람직하다고 생각하고 있던 나는 이해가 가지 않아 많은 검색을 해보았다.
이후 많은 글에서 '클린 코드' 책을 보면 이해가 갈 거라는 답변을 보았고 클린 코드 책을 보기 시작했다.

 

위의 코드도 해당 예시에 대한 이해를 바탕으로 비슷하게 구현한 코드이고, 예시가 이해가 된다면 케바케라는 말이 납득이 될 것이다.

 

그러면 함수 추가가 많은 상황에선 비니지스로 로직 작성에 유리하고 구조(유형)에 대한 추가가 많은 상황에선 도메인 객체에 작성하는 것이 유리하다면 실제 실무에선 어떤 상황이 있을까 나름 고민을 해보았다.

 

아직 경력이 짧아 많은 경험을 해보지는 않았지만, 나의 경험상 구조(유형)에 대한 변경보다는 기능(함수)에 대한 변경이 압도적으로  잦았다.

따라서 우선 서비스단에 대부분의 로직을 작성하고, 어느 정도 제품이 완성되어 갈 때쯤 필요한 부분만 로직을 객체 내부로 옮긴 뒤 추상화하며 리팩토링 하는 것이 바람직하다는 생각이다.

 

물론 설계부터 완벽하게 DDD를 적용한다면 더 객체지향적인 코드를 짤 수 있겠지만,
도메인에 대한 설계와 구조를 잡는 것부터 많은 비용을 요구하므로
개발 시간이 항상 빠듯한 SI 프로젝트에서는 위의 방식이 더 현실적이고 적합한 방법인 거 같다.

 

반응형

'Spring' 카테고리의 다른 글

Spring REST API @Valid  (0) 2024.03.06
Spring 의존 주입 에러 상황  (0) 2024.02.20
Springboot ftp 파일 업로드/다운로드  (0) 2023.08.25
[Spring] CustomReponseEntity  (0) 2023.08.09