프로젝트 패키지 구조, 어떻게 나눌까? Layer와 Domain 접근법 👊

박준형·2024년 10월 24일

스프링 개발

목록 보기
1/20
post-thumbnail

깃허브에서 다른 사람들의 코드를 살펴보면 패키지 구조가 각자 다른 걸 확인 해 볼 수 있다.
프로젝트의 패키지 구조를 잘 설계하는 것은 유지보수와 확장성에 매우 중요하다.
특히, Spring과 같은 프레임워크에서 어떻게 구조화하느냐에 따라 팀원 간의 협업 효율성과 코드 관리가 크게 달라질 수 있다. 이번 포스팅에서는 Layered Architecture와 Domain-Centric Architecture를 비교하고, 내가 선택한 방식은 무엇인지 기록할 것이다.

1. Layered Architecture (계층 구조)

Layered 아키텍처는 애플리케이션을 기능별로 나누어, 각 계층이 고유한 역할을 수행하는 방식이다. Spring 프로젝트에서 자주 사용되며, 각 계층은 특정한 책임을 맡는다.

나도 가장 처음에 클론코딩을 하며 공부를 할 때 Layered 아키텍처로 패키지 구조를 작성했었던 기억이 있다.

1.1. Layered Architecture의 구조

Controller (프레젠테이션 계층)

역할
사용자의 요청을 받고, 서비스 계층에 이를 전달한다. HTTP 요청을 처리하며, 주로 입력 데이터 검증과 응답을 구성하는 역할을 한다.

@RestController
@RequiredArgsConstructor
public class UserController {

    private final UserService userService;
    
    @GetMapping("/users/{id}")
    public ResponseEntity<UserResponseDto> getUserById(@PathVariable Long id) {
        return ResponseEntity.ok(userService.getUserById(id));
    }
}

Service (비즈니스 로직 계층)

역할
핵심 비즈니스 로직을 처리한다. 데이터베이스와 상호작용하거나 여러 도메인을 조합하여 비즈니스 요구사항을 처리한다.

@Service
@RequiredArgsConstructor
public class UserService {
    private UserRepository userRepository;
    
    public UserResponseDto getUserById(Long id) {
        User user = userRepository.findById(id).orElseThrow(() -> new UserNotFoundException());
        return new UserResponseDto(user.getName(), user.getAge());
    }
}

Repository (데이터 접근 계층)

역할
데이터베이스와의 상호작용을 담당한다. JPA, MyBatis, 혹은 JDBC 등을 통해 데이터를 조회하고 저장한다.

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}

Domain/Entity (도메인 계층)

역할
비즈니스 로직을 담은 객체 또는 데이터베이스 테이블과 매핑되는 엔티티를 관리한다.

@Entity
@Getter
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
}

1.2. Layered Architecture의 장점

명확한 역할 구분
각 계층이 명확한 역할을 담당하므로, 코드가 직관적이다.

트랜잭션 관리 용이
서비스 계층에서 트랜잭션을 일괄적으로 처리할 수 있다.

여기서 잠깐!!
트랜잭션이란 데이터베이스에서 하나의 작업 단위를 의미하며, 주로 여러 개의 작업이 하나의 작업처럼 처리되는 것을 말한다!!!
트랜잭션은 일관성을 보장하기 위해서 매우 중요한 개념이고, 데이터베이스 작업을 수행할 때 트랜잭션 내에 있는 여러 단계의 작업이 모두 성공해야 최종적으로 완료되고, 하나라도 실패 할 시 모두 실패처리가 된다.
-> 트랜잭션은 DB에 접근하여 데이터 변경이 있을 시 사용하면 좋은데, 나는 개인적으로 데이터를 생성, 수정, 삭제하는 모든 서비스 메소드에는 @Transactional을 붙여 트랜잭션을 적용하고,
조회만 하는 메소드에는 @Transactional(readOnly = true)를 붙이는 것을 선호한다.


1.3. Layered Architecture의 단점

도메인 간의 상호작용이 복잡
여러 도메인에 걸친 비즈니스 로직을 처리할 때, 계층 간 의존성이 높아질 수 있다.

복잡한 프로젝트에서 서비스 계층에 로직이 집중됨
서비스 계층에 많은 로직이 집중되어, 복잡해질 수 있다.



2. Domain-Centric Architecture (도메인 중심 구조)

Domain-Centric 아키텍처는 비즈니스 도메인을 중심으로 프로젝트를 구조화하는 방식이다. 각 도메인이 독립적으로 기능을 수행하도록 설계되며, 도메인별로 관련된 모든 코드(Controller, Service, Repository, Domain)를 한 곳에 모아 관리한다.

2.1. Domain-Centric 구조

도메인 중심
기능별로 나누는 대신, 비즈니스 도메인(예: 사용자, 주문, 상품)별로 코드를 묶는다.

높은 응집도
각 도메인 관련 코드가 한 곳에 모여 있어, 도메인 내에서 변경이 발생할 때 쉽게 관리할 수 있다.

2.2. Domain-Centric Architecture의 장점

높은 모듈성
각 도메인이 독립적으로 관리되므로, 변경 사항이 해당 도메인에만 영향을 준다.

비즈니스 로직 집중
도메인별로 로직이 집중되므로, 비즈니스 로직을 더 명확하게 표현할 수 있다.

2.3. Domain-Centric Architecture의 단점

중복된 코드 가능성
여러 도메인에서 동일한 로직이 필요할 경우 중복 코드가 발생할 수 있다.

복잡한 도메인 간 상호작용
여러 도메인에 걸친 비즈니스 로직이 필요할 때 관리가 어려워질 수 있다.

참고로 DDD 구조라고 도메인 중심 설계 방식이 있다. 이 방식은 도메인별로 코드를 나누되, 각 도메인의 비즈니스 로직을 깊이 있게 모델링하여 도메인 자체가 고유한 규칙과 역할을 가지도록 설계하는 방식이다. 패키지 내부 구조나 사용하는 방식 모두 앞선 방식들과 다르지만, 아직 이 부분은 깊이 있게 공부하지 못해서 추후 더 학습할 계획이다.



3. 내가 선택한 구조

도메인별로 프로젝트를 나누고, 각 도메인 안에서 계층 구조를 적용하는 방식을 선택했다. 도메인별로 코드를 분리함으로써 높은 모듈성을 유지하면서도, 계층 구조를 적용하여 각 역할(Controller, Service, Repository, Dto 등)을 명확하게 나눌 수 있었다.

3.1. 선택 이유

비즈니스 로직의 명확성
각 도메인별로 로직을 분리하여 비즈니스 요구사항을 명확하게 구현할 수 있을 것 같다.

계층 구조의 장점 유지
도메인 안에서도 계층 구조를 유지함으로써 코드의 역할을 명확히 구분할 수 있을 것 같다.

유지보수성
프로젝트가 커질수록 각 도메인 안에서 수정 사항이 해당 도메인에만 집중되므로, 유지보수가 쉬워질 것이라 예상했다.

협업에 유리
같은 파트의 다른 팀원과 함께 협업을 하게 될텐데 각기 다른 도메인에 집중할 수 있어, 효율성에서 좋을 것 같다.

3.2. 변경된 구조

  • controller는 presentaion, service는 application으로 이름을 설정하였다.

  • 물론 더욱 깊은 내용은 잘 알지 못하지만 DDD의 설계 방식에 따라 도메인 객체(Entity)와 해당 객체에 대한 데이터 접근을 관리하는 리포지토리가 같은 논리적 범주에 속해 있다고 보기 때문에 repository패키지를 domain패키지 아래에 두었다.

  • 또한, global패키지를 두어 여러 도메인에 걸쳐 공통적으로 사용하거나, 어떠한 특정 도메인에 해당하지 않는 부분은 global 패키지 내에서 처리하도록 설정하였다.
    이렇게 설정 시 Domain Centric 구조에서의 단점도 해결할 수 있을 것이라고 생각이 든다!!

이런 구조로 프로젝트 진행 시, 더 가독성도 좋고 효율적인 코드 작성이 가능 할 것이라고 생각이 든다.
또한, 지금 DDD구조를 볼 때는 구조가 이해가 잘 안 가기도 하고 왜 사용하는지 아직은 느끼지 못했지만 나중에 실력이 많이 는다면 DDD 구조에 대한 공부를 좀 더 해보고 싶다!!

profile
매일 매일 성장하기

0개의 댓글