728x90

1. QueryDSL(Query Domain-Specific Language)

1.1 💡QueryDSL 이란?

  • SQL, JPQL 같은 쿼리를 Java 코드로 작성할 수 있게 해주는 타입 안전한 ORM 기반 쿼리 빌더
  • SQLJava 코드로 작성하면서, 가독성과 유지보수성을 대폭 개선할 수 있는 도구

1.2 ☝️ 왜 사용해야할까

1.2.1 SQL/JPQL의 한계 해결

  • 문법 오류 - 쿼리의 오류를 런타임에서만 확인 가능
  • 필드 이름 변경 - 엔티티 필드가 변경되면 문자열 기반 쿼리에서 이를 수동으로 수정해야 함.
  • 동적 쿼리 작성의 어려움 - 조건에 따라 쿼리를 유연하게 작성하려면 코드 복잡도 향상
  • 코드 비교
// JPQL
// 필터가 많아질수록 코드가 복잡
public List<User> findUsersDynamicJPQL(EntityManager em, Integer age, String name) {
    StringBuilder jpql = new StringBuilder("SELECT u FROM User u WHERE 1=1");

    if (age != null) {
        jpql.append(" AND u.age > :age");
    }
    if (name != null) {
        jpql.append(" AND u.name = :name");
    }

    TypedQuery<User> query = em.createQuery(jpql.toString(), User.class);
    if (age != null) query.setParameter("age", age);
    if (name != null) query.setParameter("name", name);

    return query.getResultList();
}


// QueryDSL
// BooleanBuilder 사용
public List<User> findUsersDynamicQueryDSL(JPAQueryFactory queryFactory, Integer age, String name) {
    QUser user = QUser.user;

    BooleanBuilder builder = new BooleanBuilder(); // 동적 조건 빌더
    if (age != null) {
        builder.and(user.age.gt(age));
    }
    if (name != null) {
        builder.and(user.name.eq(name));
    }

    return queryFactory
            .selectFrom(user)
            .where(builder) // 동적 조건 적용
            .fetch();
}

1.2.2 복잡한 쿼리 요구사항 처리

  • 조인 - 여러 테이블 데이터를 조합해서 처리.
  • 서브쿼리 - 쿼리 안에 또 다른 쿼리를 포함.
  • 그룹핑 및 집계 - 데이터를 그룹화하거나 통계 처리.

1.2.3 장/단점

  • 장점
    • 타입 안전한 쿼리 작성
    • 가독성 UP, 유지보수성 UP
    • 동적 쿼리 작성의 유연함
    • 다양한 쿼리 지원
  • 단점
    • 초기 세팅의 복잡성, 의존성 관리
    • Q 클래스 생성 필요
    • 러닝 커브

1.3 💁 언제 사용해야할까?

1.3.1 🙆 사용해야하는 경우

  • 동적 쿼리가 많은 프로젝트
  • 복잡한 쿼리를 자주 작성해야 하는 대규모 애플리케이션
  • 타입 안전성과 코드 가독성을 중시하는 팀 환경

1.3.2 🙅 사용하지 말아야 하는 경우

  • 프로젝트가 간단한 CRUD 중심인 경우
  • 네이티브 SQL을 주로 사용하는 환경

1.4 ✈️ 대안

1.4.1 JPA Criteria API

  • JPA 표준 스펙에 포함된 쿼리 빌더 API

✅ 장점

  • 표준 API: JPA 구현체(Hibernate 등)와 상관없이 동작.
  • 별도의 외부 라이브러리 없이 사용 가능.

❌ 단점

  • 가독성 부족: 코드가 복잡하고 장황해지기 쉬움.
  • 유지보수가 어려움.
  • 예제 코드
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);

Predicate agePredicate = cb.gt(user.get("age"), 18);
Predicate statusPredicate = cb.equal(user.get("status"), "ACTIVE");

query.select(user).where(cb.and(agePredicate, statusPredicate));
List<User> result = em.createQuery(query).getResultList();

1.4.2 Spring Data JPA Specification

  • Spring Data JPA에서 제공하는 Specification 인터페이스를 활용하면 동적 쿼리 구현

✅ 장점

  • Spring Data JPA와 통합: Spring 프로젝트와 자연스럽게 연동.
  • 동적 조건 작성이 비교적 간단.

❌ 단점

  • QueryDSL에 비해 표현력이 제한적.
  • 복잡한 쿼리 작성에는 적합하지 않음.
  • 예제코드
public class UserSpecifications {
    public static Specification<User> hasAgeGreaterThan(int age) {
        return (root, query, cb) -> cb.gt(root.get("age"), age);
    }

    public static Specification<User> hasStatus(String status) {
        return (root, query, cb) -> cb.equal(root.get("status"), status);
    }
}

// 사용
Specification<User> spec = Specification.where(UserSpecifications.hasAgeGreaterThan(18))
                                         .and(UserSpecifications.hasStatus("ACTIVE"));

List<User> users = userRepository.findAll(spec);

1.4.3 네이티브 SQL

✅ 장점

  • 유연성: 데이터베이스에 특화된 쿼리를 작성 가능
  • 성능 최적화에 유리

❌ 단점

  • 타입 안전성 부족: 문자열 기반으로 작성
  • 데이터베이스 종속적 → 이식성이 낮음
  • 유지보수가 어려움
  • 예제 코드
@Query(value = "SELECT * FROM users WHERE age > :age AND status = :status", nativeQuery = true)
List<User> findUsersNative(@Param("age") int age, @Param("status") String status);

1.4.4 jOOQ (Java Object Oriented Querying)

  • jOOQ는 SQL을 Java 코드로 작성하면서도, 데이터베이스와 밀접하게 통합하여 고급 기능을 사용할 수 있는 강력한 도구

✅ 장점

  • SQL 친화적 설계: SQL의 고급 기능(윈도우 함수, CTE 등)을 Java 코드로 직접 활용 가능.
  • 타입 안전성: SQL 컬럼과 Java 필드 간의 타입을 컴파일 타임에 검증.
  • DBMS 특화 기능 지원: 데이터베이스에 따라 최적화된 SQL 생성 가능.
  • 코드 자동 생성: 테이블 스키마 기반으로 Java 코드를 생성하여, 변경 사항을 자동 반영.

❌ 단점

  • SQL 종속성: 데이터베이스 변경 시 쿼리 코드 수정이 필요.
  • 학습 난이도: SQL에 익숙하지 않은 개발자에게는 진입 장벽이 높음.
  • ORM 부족: 객체 지향적 추상화가 부족하고, SQL 중심의 설계에 초점.
  • 예제 코드
// 동적 조건 추가
public List<User> findUsersDynamicJooq(DSLContext context, Integer age, String name) {
    Condition condition = DSL.trueCondition();

    if (age != null) {
        condition = condition.and(USER.AGE.gt(age));
    }
    if (name != null) {
        condition = condition.and(USER.NAME.eq(name));
    }

    return context.selectFrom(USER)
                  .where(condition)
                  .fetchInto(User.class);
}

 

 

라이브러리 특징 장점 단점
QueryDSL - 타입 안전한 동적 쿼리 작성을 지원
- Q클래스를 통해 메서드 체인 방식으로 쿼리를 작성
- 타입 안전성(Type Safety) 보장
- 가독성 높은 코드 작성 가능
- IDE 자동완성 지원
- 초기 설정 복잡
- Q클래스 생성 필요
Spring Data JPA Specifications - JPA의 Criteria API를 기반으로 동적 쿼리를 작성
- Specification 인터페이스를 사용
- 추가 라이브러리 없이 Spring Data JPA와 자연스러운 통합
- 동적 쿼리 작성 가능
- 코드가 장황하고 가독성이 떨어질 수 있음
JOOQ - SQL 빌더 라이브러리로, SQL 문법을 코드로 표현
- 고급 SQL 기능을 지원
- SQL 문법을 코드로 직관적으로 표현 가능
- 복잡한 쿼리와 고급 SQL 기능 활용 가능
- JPA와 통합하려면 추가 설정 필요
Criteria API (JPA 기본) - JPA 표준 사양
- 메서드 체인 방식을 통해 동적 쿼리를 작성
- 모든 JPA 구현체에서 사용 가능
- 추가 의존성 필요 없음
- 코드가 복잡하고 가독성이 떨어짐
Native QSL - SQL 문법그대로 쿼리 작성 - 데이터베이스에 특화된 쿼리를 작성 가능
- 성능 최적화에 유리
- 데이터베이스 종속적 → 이식성이 낮음
- 유지보수가 어려움

 

반응형

'Study > Spring' 카테고리의 다른 글

[Spring] Spring Security + OAuth 2.0 소셜 로그인  (1) 2025.01.16
[Spring Boot] DB Lock  (0) 2025.01.10
[Spring] @InitBinder  (0) 2021.08.08
[Spring] @Data 어노테이션  (0) 2021.06.30
[Spring] 영속성 컨텍스트 (Persistence Context)  (0) 2021.06.30
728x90

2.1 네 개의 영역

  • 표현 영역
    • Controller 부분
    • 사용자와 상호 작용하는 영역
    • HTTP 요청 ↔ 응용 영역이 서로 통신할 수 있도록 요청과 응답을 변환
  • 응용 영역
    • Service 부분
    • 시스템이 사용자에게 제공해야 할 기능을 구현
    • 기능을 구현하기 위해 도메인 영역의 도메인 모델을 사용
  • 도메인 영역
    • Entity, Value 부분
    • 도메인의 핵심 로직을 구현
      • ex) Order 도메인의 핵심 기능은 ‘배송지 변경’, ‘결제 완료’ … 등이 있으며 해당 로직을 구현한다.
  • 인프라스트럭처 영역
    • Repository, Utils 부분
    • 도메인 로직을 지원하고 구현을 외부 시스템과 연결하는 기술적 구조
      • ex) DB 연결, STMP 메일발송, 메세지큐 … 등

2.2 계층 구조 아키텍처

  • 상위 계층에서 하위 계층으로 의존만 존재하고 하위 계층에서 상위 계층에 의존하지 않는다.

2.3 DIP

  • DIP 란?
    • DIP(Dependency Inversion Principle, 의존성 역전 원칙)은 객체 지향 설계 원칙 중 하나로, 고수준 모듈이 저수준 모듈에 의존해서는 안 되고, 추상화를 통해 저수준 모듈이 고수준 모듈을 의존
  • 장점
    • 저수준 모듈을 교체 하기가 용이
    • 테스트코드를 구현하기에 용이
      • 저수준 모듈의 구현체가 없는 추상화(Interface)만 존재하는 경우에도 테스트 코드 작성이 가능하다 (Mock 이용)

2.3.1 DIP 주의사항

  • 단순히 인터페이스와 구현 클래스를 분리하는 것은 잘못된 사용
  • 결과 구조만 보는것이 아닌 모듈의 관점에서 생각하여 추상화를 만들어야 한다.
    • ex)
      1. cacluateDiscountService → RuleEngine ← DroolsRuleEngine (잘못된 사용)
      2. cacluateDiscountService → RuleDiscounter ← DroolsRuleDiscounter (good)

2.3.2 DIP와 아키텍처

  • DIP를 적용하게 되면, 응용 영역과 도메인 영역의 코드 수정 없이 인프라스트럭처부분만 코드를 추가/수정 및 변경하여 요구사항을 충족시킬 수 있다.

2.4 도메인 영역의 주요 구성요소

구성요소 설명 예시
엔티티(Entity) 고유 식별자를 가지며, 상태와 동작을 포함하는 객체 User, Order, Product
값 객체(Value Object) 고유 식별자가 없으며, 불변 객체로 주로 여러 속성을 하나의 개념으로 묶을 때 사용 Address, Money, DateRange
애그리게잇(Aggregate) 관련된 엔티티와 값 객체의 그룹으로, 일관된 변경을 보장하기 위한 경계를 정의하고 루트 엔티티를 통해 접근 Order (루트: Order, 구성 요소: OrderLine)
리포지토리(Repository) 애그리게잇을 영구 저장소에서 조회하고 저장하는 메커니즘을 제공, 인터페이스와 구현체로 구성 OrderRepository, UserRepository
도메인 서비스(Domain Service) 특정 엔티티에 속하지 않는 도메인 로직을 캡슐화하는 서비스 PricingService, PaymentService

2.4.1 엔티티와 벨류

  • 도메인 모델의 엔티티 vs DB 테이블의 엔티티
    • 도메인 모델의 엔티티는 단순 데이터만 담고 있는 구조가 아닌 데이터와 함께 도메인 기능을 제공
    • 도메인 모델 두개이상의 데이터가 개념적으로 하나인 경우Value 타입을 이용해서 표현할 수 있다.
      • ex) Order → name, email 을 Orderer라는 Value타입 객채를 만들어 관리 가능

2.4.2 애그리거트

  • 지도를 볼 때 매우 상세하게 나온 대축척 지도를 보면 큰 수준에서 어디에 위치하고 있는지 이해하기 어려우므로 큰 수준에서 보여주는 소축척 지도를 함께 봐야 현재 위치를 보다 정확하게 이해할 수 있다. 이와 비슷하게 도메인 모델도 개별 객체뿐만 아니라 상위 수준에서 모델을 볼 수 있어야 전체 모델의 관계와 개별 모델을 이해하는데 도움이 된다.
    • ex) ‘주문’, ‘배송지 정보’, ‘주문자’, ‘주문 목록’, ‘총 결제 금액’의 하위 모델들을 ‘주문’이라는 상위 개념으로 표현할 수 있다.

2.5 요청 처리 흐름

2.6 인프라스트럭처 개요

  • 표현 영역, 응용 영역, 도메인 영역을 지원한다.
  • DIP에서 언급한 내용처럼 인프라스트럭처의 기능을 직접 사용하는것보다 인터페이스를 만들어서 사용하게 되면 시스템을 더 유연하고 테스트하기 쉽게 만들어준다. 하지만 무조건 인프라스트럭처에 대한 의존을 없애게 되면 오히려 더 복잡하고 어려운 코드를 유도할 수 있다.
    • ex) 인프라 스트럭처를 직접 사용하지 않기 위해 @Transaction 어노테이션을 사용하지 않고 개발을 하려고 하면 한줄로 처리할 수 있는 코드를 복잡하고 개발시간만 더 늘어날 뿐이다.

2.7 모듈 구성

  • 도메인이 크면 하위 도메인으로 나누고 각 하위 도메인 마다 별도 패키지를 구성한다.
  • 모듈 구성에 대해서는 정답이 없으며, 한 패키지에 10~15개 미만으로 타입 개수를 유지하려고 노력한다.

반응형

'Study > DDD' 카테고리의 다른 글

[DDD] DDD 용어 정리  (1) 2024.12.20
[DDD] Chapter3. 애그리거트  (0) 2024.06.03
728x90

작성 목적

해당 게시글은 DDD관련하여 공부를 하던 중 개인적으로 모르거나 햇갈리는 용어에 대해 리마인드 하기 위해 요약한 내용입니다.

잘못 이해하고 있는 내용이 있으면 댓글로 남겨주시면 감사하겠습니다.

(DDD관련 학습을 진행하는 동안 지속적으로 업데이트 할 예정)

 

용어

  • 도메인 주도 설계(DDD(Domain-Driven Design))
    • 복잡한 소프트웨어를 설계 및 개발하는 과정에서 핵심적인 비지니스 개념과 비지니스 논리 중심으로 설계하는 방법
  • 도메인 전문가
    • 특정 분야나 사업에 대해 깊이있는 지식과 경험을 가진 사람
  • 개념모델 vs구현 모델
    • 개념모델 : "무엇"을 할 것인지에 대한 전략적 설계를 제공
    • 구현모델 : "어떻게" 실행할 것인지에 대한 실용적인 가이드를 제공
  • 유비쿼터스 언어 (보편언어)
    • 팀원 모두가 동일하게 이해하고 표현하는 공통적인 언어

참고 자료

반응형

'Study > DDD' 카테고리의 다른 글

[DDD] Chapter 2 아키텍처 개요  (0) 2024.12.20
[DDD] Chapter3. 애그리거트  (0) 2024.06.03

+ Recent posts