List
Search
1. 도메인 레이어 구현 방식
•
도메인 레이어의 구현 방식
◦
트랜잭션 스크랩트 패턴
◦
도메인 모델 패턴
classDiagram
class Process {
+process()
}
class Data {
+getter
+setter
}
Process ..> Data : usesMermaid
복사
트랜잭션 스크립트 패턴
classDiagram
class Object {
+field
+method()
}
class ChildObject1
class ChildObject2
Object <|-- ChildObject1
Object <|-- ChildObject2Mermaid
복사
도메인 모델 패턴
1.1. 트랜잭션 스크립트 패턴
1.1.1. 정의
•
프로세스와 데이터 분리
•
도메인 로직을 절차지향 방식으로 구현
•
빈약한 도메인 모델의 데이터를 이용해서 로직 처리
1.1.2. 도메인 레이어
•
단일 프로시저로 구성되며, 서비스 레이어와 도메인 레이어를 별도로 분리하지 않음
•
Fat Service
1.1.3. 퍼시스턴스 레이어
•
테이블 데이터 게이트웨이 (Table Data Gateway)
◦
하나의 데이터베이스 테이블에 대한 게이트웨이 역할을 수행
◦
단일 테이블 뷰에 접속하는 SQL 처리
•
데이터 접속 객체 (Data Access Object)
◦
하나의 테이블 또는 뷰에 대한 영속성 로직을 담당하는 객체
◦
테이블 데이터 게이트웨이 패턴의 또다른 이름
classDiagram
class MovieDAO {
+find(id): Movie
+findWithTitle(title: String): Movie
+update(movie)
+insert(movie)
+delete(id)
}
class MOVIE_DB {
ID
TITLE
RUNNING_TIME
FEE_AMOUNT
}
MovieDAO ..> MOVIE_DB : access DB Table
Mermaid
복사
1.2. 도메인 모델 패턴
1.2.1. 정의
•
프로세스와 데이터 통합
•
도메인 로직을 객체지향 방식으로 구현
•
상태와 행위를 포함하는 객체 사이의 협력을 통해 기능 구현
1.2.2. 도메인 모델
•
추상적으로 도메인을 이렇게 봅시다 라고 정의한 모델
classDiagram
class Screening {
+reserve(customer, audienceCount)
}
class Reservation
class Customer
class Movie {
+calculateFee(screening)
}
class DiscountPolicy {
+calculateDiscount(screening)
}
class AmountDiscountPolicy
class PercentDiscountPolicy
class DiscountCondition {
<<interface>>
+isSatisfiedBy(screening)
}
class SequenceCondition
class PeriodCondition
Screening "1" --> "*" Reservation : create
Reservation "*" --> "1" Customer
Screening "*" --> "1" Movie
Movie "1" --> "0..1" DiscountPolicy
DiscountPolicy <|-- AmountDiscountPolicy
DiscountPolicy <|-- PercentDiscountPolicy
DiscountPolicy "1" --> "1..*" DiscountCondition
DiscountCondition <|.. SequenceCondition
DiscountCondition <|.. PeriodCondition
Mermaid
복사
1.2.3. 서비스 레이어
•
어플리케이션 경계
•
어플리케이션 로직
•
도메인 로직의 재사용성 촉진
1.2.4. 도메인 레이어
•
객체지향으로 작성되어야할 도메인 로직을 담당
1.2.5. 객체 관계 임피던스 불일치 (Object-Relational Impedance Mismatch)
•
객체 모델과 DB 스키마 사이의 불일치
•
객체 패러다임과 관계 패러다임 간의 불일치
•
테이블보다 더많은 클래스를 뽑음
•
복잡함 → 맵핑 필요
classDiagram
%% Object Model
class DiscountStrategy
class AmountStrategy
class PercentStrategy
class NonDiscountStrategy
class Rule
class SequenceRule
class TimeOfDayRule
DiscountStrategy <|-- AmountStrategy
DiscountStrategy <|-- PercentStrategy
DiscountStrategy <|-- NonDiscountStrategy
DiscountStrategy --> Rule
Rule <|-- SequenceRule
Rule <|-- TimeOfDayRule
%% Relational Model
class DISCOUNT {
MOVIE_ID (FK)
DISCOUNT_TYPE
FEE_AMOUNT
FEE_CURRENCY
PERCENT
}
class RULE {
ID
DISCOUNT_ID (FK)
POSITION
RULE_TYPE
DAY_OF_WEEK
START_TIME
END_TIME
SEQUENCE
}
DISCOUNT "1" <-- "0..*" RULE : contains
Mermaid
복사
1.2.6. 데이터 매퍼 (Data Mapper)
•
객체 모델과 DB 스키마 간의 의존성 끊고 중간에서 관리
•
객체 모델과 DB 스키마 간의 독립성 유지
1.2.7. 객체 관계 맵핑 (Object Relation Mapping, ORM)
•
객체와 데이터베이스 테이블 사이의 맵핑 기법 또는 맵핑 도구
•
일반적으로 재사용 가능한 데이터 맵핑 프레임워크 사용
1.2.8. 리파지토리 (Repository)
•
데이터 매퍼 용도로 사용되는 객체
•
일반적으로 데이터 접근 객체의 개념을 포함
1.3. JPA
•
자바에서 사용되는 ORM
•
범용적인 기능을 제공함 → 어떻게 사용하냐에 따라 복잡도나 난이도가 높아짐
1.3.1. 트랜잭션 스크립트 패턴과 JPA
•
SQL 자동화의 목적으로 단순하게 쓰는 것이 권장됨
•
Lazy Loading, 연관관계 맵핑 등 복잡한 기술을 쓰는 순간 복잡도가 어려워짐
•
따라서 쿼리 자동 생성 목적으로 JPA를 사용할 것
◦
퍼시스턴스 레이어가 Table Data Gateway(DAO) 역할만 수행
classDiagram
%% 도메인 레이어
class Process {
+process()
}
class Data {
+getter()
+setter()
id
title
runningTime
feeAmount
}
%% 관계
Process --> Data : useMermaid
복사
트랜잭션 스크립트 패턴의 도메인 레이어
classDiagram
%% 퍼시스턴스 레이어
class MovieDAO {
+find(id): Movie
+findWithTitle(title: String): Movie
}
class MOVIE {
ID
TITLE
RUNNING_TIME
FEE_AMOUNT
}
%% 관계
MovieDAO ..> MOVIE : 비슷한 형태의 객체테이블 변환. \n SQL 자동화
Mermaid
복사
트랜잭션 스크립트 패턴의 영속성 레이어
1.3.2. 도메인 모델 패턴과 JPA
•
객체 지향 / 도메인 모델 패턴을 쓴다면 임피던스 불일치를 해결하기 위해 사용
•
결국 JPA 라는 ORM을 통해 임피던스 불일치를 해결하겠다는 것
classDiagram
class DiscountStrategy
class AmountStrategy
class PercentStrategy
class NonDiscountStrategy
class Rule
class SequenceRule
class TimeOfDayRule
DiscountStrategy <|-- AmountStrategy
DiscountStrategy <|-- PercentStrategy
DiscountStrategy <|-- NonDiscountStrategy
DiscountStrategy --> Rule
Rule <|-- SequenceRule
Rule <|-- TimeOfDayRule
%% === DB 테이블 ===
class DISCOUNT {
MOVIE_ID (FK)
DISCOUNT_TYPE
FEE_AMOUNT
FEE_CURRENCY
PERCENT
}
class RULE {
ID
DISCOUNT_ID (FK)
POSITION
RULE_TYPE
DAY_OF_WEEK
START_TIME
END_TIME
SEQUENCE
}
DISCOUNT "1" <-- "0..*" RULE : contains
DiscountStrategy <.. DISCOUNT : JPA로 임피던스 불일치 해결
Mermaid
복사
2. 트랜잭션 스크립트 패턴 예시 (영화 예매 시스템)
•
2.1. 시스템 요구사항/도메인 분석
2.1.1. 도메인 개념
•
영화 (Movie)
◦
기본 정보 (공통의 메타 정보)
•
상영 (Screening)
◦
실제로 시스템에서 관객들이 구매하는 상품
◦
예) 2025.04.19 09:30
•
할인 정책
◦
금액 할인 정책
▪
1천원 할인
◦
비율 할인 정책
▪
10% 할인
•
할인 조건
◦
순서 조건
▪
조조 상영인 경우
▪
10회 상영인 경우
◦
기간 조건
▪
월요일 10:00 ~ 12:00 상영인 경우
▪
목요일 18:00 ~ 21:00 상영인 경우
•
예매
◦
제목, 상영시간, 인원
◦
정가, 결제 금액등
2.1.2. 도메인 개념 요약
classDiagram
class Screening {
<<상영>>
}
class Reservation {
<<예매>>
}
class Movie {
<<영화>>
}
class DiscountPolicy {
<<할인 정책>>
}
class DiscountCondition {
<<할인 조건>>
}
Screening "1" --> "0..*" Reservation
Screening "0..*" --> "1" Movie
Movie "1" --> "0..1" DiscountPolicy
DiscountPolicy "1" --> "1..*" DiscountCondition
Mermaid
복사
2.2. 설계 및 구현
classDiagram
class Process {
+process()
}
class Data {
+getter
+setter
}
Process ..> Data : usesMermaid
복사
2.2.1. 데이터 (객체)
•
무엇을 저장할 것인가?에 대한 부분
•
도메인 개념을 저장할 각각의 빈약한 도메인 모델들이 도출됨
•
구현
◦
메세드를 통해 내부 캡슐화 진행
◦
필드는 클래스 내부에 캡슐화
◦
어떠한 상황에서도 제한없이 접근가능하도록 모든 필드에 getter / setter 추가
@NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor
@Getter @Setter
public class Movie {
private Long id;
private Long policyId;
private String title;
private Integer runningTime;
private Long fee;
public Movie(Long policyId, String title, Integer runningTime, Long fee) {
this.policyId = policyId;
this.title = title;
this.runningTime = runningTime;
this.fee = fee;
}
}
Java
복사
2.2.2. 프로세스 (알고리즘, 도메인 로직)
•
어떻게 처리할 것인가에 대한 부분
•
DAO → 영속성 저장소와의 상호작용을 담당하는 객체
•
프로세스 정의
◦
Service 클래스 안에 프로세스를 구현하기 위한 메서드 정의
◦
이후 절차적으로 구현 진행
package org.eternity.script.movie.service;
import jakarta.transaction.Transactional;
import org.eternity.script.movie.domain.*;
import org.eternity.script.movie.persistence.*;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ReservationService {
private ScreeningDAO screeningDAO;
private MovieDAO movieDAO;
private DiscountPolicyDAO discountPolicyDAO;
private DiscountConditionDAO discountConditionDAO;
private ReservationDAO reservationDAO;
public ReservationService(ScreeningDAO screeningDAO,
MovieDAO movieDAO,
DiscountPolicyDAO discountPolicyDAO,
DiscountConditionDAO discountConditionDAO,
ReservationDAO reservationDAO) {
this.screeningDAO = screeningDAO;
this.movieDAO = movieDAO;
this.discountConditionDAO = discountConditionDAO;
this.discountPolicyDAO = discountPolicyDAO;
this.reservationDAO = reservationDAO;
}
@Transactional
public Reservation reserveScreening(Long customerId, Long screeningId, Integer audienceCount) {
// 1. 데이터베이스 조회
Screening screening = screeningDAO.selectScreening(screeningId);
Movie movie = movieDAO.selectMovie(screening.getMovieId());
DiscountPolicy policy = discountPolicyDAO.selectDiscountPolicy(movie.getId());
List<DiscountCondition> conditions = discountConditionDAO.selectDiscountConditions(policy.getId());
// 2. 할인 여부 판단
DiscountCondition condition = findDiscountCondition(screening, conditions);
// 3. 할인 요금 계산
Long fee;
if (condition != null) {
fee = movie.getFee() - calculateDiscount(policy, movie);
} else {
fee = movie.getFee();
}
// 4. 예매 생성
Reservation reservation = makeReservation(customerId, screeningId, audienceCount, fee);
reservationDAO.insert(reservation);
return reservation;
}
}
Java
복사
2.3. 요약
•
중앙 집중식 제어 스타일
sequenceDiagram
participant RS as :ReservationService
participant SDAO as :ScreeningDAO
participant DCDAO as :DiscountConditionDAO
participant DPDAO as :DiscountPolicyDAO
participant DC as :DiscountCondition
participant S as :Screening
participant DP as :DiscountPolicy
participant R as :Reservation
RS->>RS: reserveScreening()
RS->>SDAO: selectScreening()
SDAO-->>RS:
RS->>DCDAO: selectDiscountConditions()
DCDAO-->>RS:
RS->>DPDAO: selectDiscountPolicy()
DPDAO-->>RS:
RS->>DC: isPeriodCondition()
DCDAO-->>RS:
RS->>S: playedIn()
SDAO-->>RS:
RS->>DC: isAmountCondition()
DCDAO-->>RS:
RS->>DP: getAmount()
DPDAO-->>RS:
RS->>DC: isPercentCondition()
DCDAO-->>RS:
RS->>DP: getPercent()
DPDAO-->>RS:
RS->>R: new
Mermaid
복사
•
낮은 응집도
◦
단일 책임 원칙이 지켜지지 않음
◦
빈약한 모델의 내부 필드가 변경될 이유가 너무 많음
•
높은 결합도
◦
DB, 도메인 로직 등과 강하게 결합되어 있음 (읽고 처리하고 등등..)
•
아키텍처 예시
classDiagram
class ReservationController {
+reserveScreening()
}
class ReservationService {
+reserveScreening()
}
class MovieDAO {
<<interface>>
+selectMovie(movieId)
+insert(movie)
}
class ScreeningDAO {
<<interface>>
+selectScreening(screeningId)
+insert(screening)
}
class DiscountPolicyDAO {
<<interface>>
+selectDiscountPolicy(movieId)
+insert(discountPolicy)
}
class DiscountPolicy {
id
movieId
policyType
amount
percent
+getter/setter
}
ReservationController --> ReservationService
ReservationService --> MovieDAO
ReservationService --> ScreeningDAO
ReservationService --> DiscountPolicyDAO
MovieDAO ..> DiscountPolicy
ScreeningDAO ..> DiscountPolicy
DiscountPolicyDAO ..> DiscountPolicy
Mermaid
복사