7. Domain Concept – 할인 규칙
Sequence Rule
Rule
조조 상영인 경우
10회 상영인 경우
Time Rule
월요일 10:00 ~ 12:00 상영인 경우
화요일 18:00 ~ 21:00 상영인 경우
7 / 객체지향적인 도메인 레이어 구축하기
8. Domain Concept –할인 정책 + 할인 규칙
조조 상영인 경우
10회 상영인 경우
도가니 Amount DC
8000원 800원
월요일 10:00 ~ 12:00 상영인 경우
화요일 18:00 ~ 21:00 상영인 경우
Movie Discount Rule
1 0..1 1 1..*
8 / 객체지향적인 도메인 레이어 구축하기
9. Domain Concept –예매
Reservation
제 목 도가니
2011년 10월 18일 (목)
상영 정보 7회 6:00(오후) – 8:00(오후)
인 원 2명
정 가 16,000원
결재 금액 14,400원
9 / 객체지향적인 도메인 레이어 구축하기
10. Domain Model
Showing Reservation
1 0..*
0..*
1
1 0..1 1 1..*
Movie Discount Rule
10 / 객체지향적인 도메인 레이어 구축하기
11. 영화 예매 책임 수행을 위한 협력의 설계
후보 객체Candidate, 책임Responsibility, 협력Collaboration 식별
Movie
Showing 영화 정보를 알고 있다 Discount
Strategy
가격을 계산한다
상영 정보를 알고 있다 Movie
예매 정보를 생성한다 DiscountStrategy
할인율 정책을 알고 있다 Rule
할인된 가격을 계산한다
Rule
할인 정책을 알고 있다 Showing
할인 여부를 판단한다
11 / 객체지향적인 도메인 레이어 구축하기
12. 도메인 레이어 객체 구현
User Interface
Service
Reservation Domain
Showing
<<create>> Infrastructure
reserve(customer, count):Reservation
Customer
{abstract} {abstract}
Movie DiscountStrategy Rule
calculateFee(showing):Money calculateFee(showing):Money isStatisfiedBy(showing):boolean
Amount Percent NonDiscount Sequence TimeOfDay
Strategy Strategy Strategy Rule Rule
12 / 객체지향적인 도메인 레이어 구축하기
13. Domain Model
Showing Reservation
1 0..*
0..*
1
1 0..1 1 1..*
Movie Discount Rule
13 / 객체지향적인 도메인 레이어 구축하기
18. 객체-관계 임피던스 불일치
객체 모델과 DB 스키마 간의 불일치
객체 패러다임Object Paradigm과 관계 패러다임Relational Paradigm 간의 불일치
RULE
DISCOUNT
DiscountStrategy Rule ID
MOVIE_ID(FK)
DISCOUNT_ID(FK)
DISCOUNT_TYPE POSITION
FEE_AMOUNT RULE_TYPE
FEE_CURRENCY DAY_OF_WEEK
Amount Percent NonDiscount Sequence TimeOfDay PERCENT START_TIME
Strategy Strategy Strategy Rule Rule END_TUME
SEQUENCE
18 / 객체지향적인 도메인 레이어 구축하기
19. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
19 / 객체지향적인 도메인 레이어 구축하기
20. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
20 / 객체지향적인 도메인 레이어 구축하기
21. Mismatch 구성 요소 크기Granularity의 불일치
MOVIE
ID(PK)
TITLE
RUNNING_TIME
Movie FEE_AMOUNT
FEE_CURRENCY
제목
하나와 앨리스
시간
135분
<<value object>>
running Duration
Time
요금 quantity
<<entity>>
8,000원
Movie
title <<value object>>
calculateFee(showing):Money
Money
amount
currency
fee
21 / 객체지향적인 도메인 레이어 구축하기
22. Entity & Value Object
Identity Value
Value Object
Entity <<value object>>
running Duration
Time
quantity
<<entity>>
Movie
title <<value object>>
calculateFee(showing):Money Money
amount
currency
fee
22 / 객체지향적인 도메인 레이어 구축하기
23. 책임의 재사용
Money
금액과 환율을 알고 있다
Movie 금액을 +,-,*,/한다
영화 정보를 알고 있다
가격을 계산한다 Duration
기간을 알고 있다.
기간의 합, 차를 계산한다
23 / 객체지향적인 도메인 레이어 구축하기
24. Problem 코드 중복Code Duplication
RESERVATION
MOVIE
ID(PK)
ID(PK)
CUSTOMER_ID(FK)
TITLE SHOWING_ID(FK)
RUNNING_TIME FEE_AMOUNT
FEE_AMOUNT FEE_CURRENCY
FEE_CURRENCY AUDIENCE_COUNT
class Movie {
long plus(long fee, Currency currency) {
if (this.feeCurrency.equals(currency) {
return this.fee + fee;
}
Movie throw new IllegalArgumentException(); Reservation
}
id } id
title customerId
runningTime showingId
fee class Reservation { amount
long add(long amount, Currency currency) {
feeCurrency acmointCurrency
if (this.amountCurrency.equals(currency) {
return this.amount + fee; audienceCount
calculateFee() }
getFee()
throw new IllegalArgumentException();
}
}
24 / 객체지향적인 도메인 레이어 구축하기
25. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
25 / 객체지향적인 도메인 레이어 구축하기
26. Mismatch 데이터베이스 식별자Database Identity
테이블 주키Primary Key
제목 제목
시간을 시간을
달리는 소녀 달리는 소녀
시간 시간
95분 95분
요금 요금
8,000원 8,000원
MOVIE
ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY
1 시간을 달리는 소녀 95 8,000 KRW
2 시간을 달리는 소녀 95 8,000 KRW
26 / 객체지향적인 도메인 레이어 구축하기
27. 객체 식별자Object Identity
Java == 연산자
제목 제목
시간을 시간을
달리는 소녀 달리는 소녀
시간 시간
95분 95분
요금 요금
8,000원 8,000원
address 1 address 2
movie1 : Movie movie2 : Movie
title = ‚시간을 달리는 소녀‛ title = ‚시간을 달리는 소녀‛
runningTime = 95 runningTime = 95
Fee = 8000 Fee = 8000
27 / 객체지향적인 도메인 레이어 구축하기
28. Problem 데이터베이스-객체 식별자 불일치Identity Mismatch
MOVIE
ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY
1 시간을 달리는 소녀 95 8,000 KRW
2 시간을 달리는 소녀 95 8,000 KRW
movie1 : Movie movie2 : Movie
title = ‚시간을 달리는 소녀‛ title = ‚시간을 달리는 소녀‛
runningTime = 95 runningTime = 95
Fee = 8000 Fee = 8000
28 / 객체지향적인 도메인 레이어 구축하기
29. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
29 / 객체지향적인 도메인 레이어 구축하기
30. Mismatch 다형성Polymorphism
{abstract}
Movie DiscountStrategy
Movie Discount calculateFee(showing) registerDate
calculateFee(showing)
Amount Discount 800원
Amount Percent Non
DiscountStrategy DiscountStrategy DiscountStrategy
Percent Discount 10%
discountAmount percent
public class Movie {
No Discount 0원(%) private DiscountStrategy discountStrategy;
public Money calculateFee(Showing showing) {
return discountStrategy.calculateFee(showing);
}
}
30 / 객체지향적인 도메인 레이어 구축하기
31. Problem 젃차적인 조건 분기Conditional Branch
{abstract}
Movie Movie DiscountStrategy
DiscountStrategy
id movieId
Movie Discount calculateFee(showing)
title
registerDate
discountType
runningTime calculateFee(showing)
amount
fee percent
Amount Discount 800원 feeCurrency registerDate
calculateFee()
calculateFee()
Amount Percent Non
DiscountStrategy DiscountStrategy DiscountStrategy
Percent Discount 10%
discountAmount percent
public class ReservationService {
public class Movie {
public Money calculateFee(Movie movie,
No Discount 0원(%) private DiscountStrategy discountStrategy) {
DiscountStrategy discountStrategy;
if (discountStrategy.isAmountType()) {
...
public Money calculateFee(Showing showing) {
} else if (discountStrategy.isPercentType()) {
return discountStrategy.calculateFee(showing);
} ...
} else {
} ...
}
...
}
}
31 / 객체지향적인 도메인 레이어 구축하기
32. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
32 / 객체지향적인 도메인 레이어 구축하기
33. Mismatch 데이터베이스 외래키Foreign Key
MOVIE SHOWING
ID(PK) ID(PK)
TITLE MOVIE_ID(FK)
RUNNING_TIME SEQUENCE
Movie Showing FEE_AMOUNT SHOWING_TIME
FEE_CURRENCY
1회 10/18 10:00
2회 10/18 12:30
MOVIE
3회 ID TITLE
10/18 15:00
1 하나와 앨리스
4회 10/18 17:30
SHOWING
ID MOVIE_ID SEQUENCE SHOWING_TIME
1 1 7 2010-12-23 18:00
33 / 객체지향적인 도메인 레이어 구축하기
34. 테이블 외래키Foreign Key - 양방향BiDirectional
MOVIE SHOWING
ID(PK) ID(PK)
TITLE MOVIE_ID(FK)
RUNNING_TIME SEQUENCE
FEE_AMOUNT SHOWING_TIME
FEE_CURRENCY
SELECT *
FROM MOVIE as m LEFT JOIN SHOWING as s
ON m.ID = s.MOVIE_ID
34 / 객체지향적인 도메인 레이어 구축하기
35. 객체 연관관계Association – 단방향UniDirectional
MOVIE SHOWING
ID(PK) ID(PK)
TITLE MOVIE_ID(FK)
RUNNING_TIME SEQUENCE
FEE_AMOUNT SHOWING_TIME
FEE_CURRENCY
Movie Showing
showing.getMovie();
calculateFee(showing) 1 * reserve(customer, count)
Movie Showing
movie.getShowings();
calculateFee(showing) 1 * reserve(customer, count)
Movie Showing movie.getShowings();
calculateFee(showing) 1 * reserve(customer, count) showing.getMovie();
35 / 객체지향적인 도메인 레이어 구축하기
36. 객체 협력Collaboration을 통한 구현
연관 관계는 책임 분배와 협력을 위한 핵심 메커니즘
Showing
Showing
상영 정보를 알고 있다 Movie
reserve()
예매 정보를 생성한다
*
Movie
영화 정보를 알고 있다 Discount
Strategy
Movie
가격을 계산한다 1
calculateFee()
DiscountStrategy 1
할인율 정책을 알고 있다 Rule
할인된 가격을 계산한다
DiscountStrategy
0..1
calculateFee()
public class Showing {
private Movie movie; public class Movie {
private DiscountStrategy discountStrategy;
public Money calculateFee() {
return movie.calculateFee(this); public Money calculateFee(Showing showing) {
} return discountStrategy.calculateFee(showing);
... }
} ...
}
36 / 객체지향적인 도메인 레이어 구축하기
37. Problem Data + Process = 젃차적인Procedural 프로그래밍
MOVIE SHOWING
ID(PK) ID(PK)
TITLE MOVIE_ID(FK)
RUNNING_TIME SEQUENCE
FEE_AMOUNT SHOWING_TIME
FEE_CURRENCY
Process
Movie Showing Data
id id
ReservationService Movie Showing
title movieId
reserveShowing() runningTime title
fee
calculateFee(showing)
1 * sequence
reserve(customer, count)
feeCurrency showingTime
public class ReservationService {
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
public class Showing showingDAO.selectShowing(showingId);
Showing showing = {
Movie movie = movieDAO.selectMovie(showing.getMovieId());
private Movie movie;
long amount = calculateFee(movie.getFee(), showing, audienceCount);
public Money calculateFee() {
...
} return movie.calculateFee(this);
}
private long calculateFee(long movieFee, Showing showing, int audientCount) {
...
} ...
}
}
37 / 객체지향적인 도메인 레이어 구축하기
38. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
38 / 객체지향적인 도메인 레이어 구축하기
40. 할인 규칙 등록
{abstract} {abstract}
Showing Movie DiscountStrategy Rule
reserve(customer, count) 1 * calculateFee(showing) 1 0..1 calculateFee(showing) 1 0..* calculateFee(showing)
Movie 캡슐화 경계Boundary
제 목 무지개여싞
상영 시간 116분 요 금 8,000원
AMOUNT
Discount
할인 정책 DISCOUNT 할인 금액 9,000원
할인 규칙 SEQUENCE RULE
Rule
할인 대상 1회 1회 5회 7회
40 / 객체지향적인 도메인 레이어 구축하기
41. Problem 캡슐화 저해
Flat 불변식 위반Invariant Violation
Movie movie = movieDAO.selectMovie(1);
movie.setFee(8000);
DiscountStrategy strategy =
new AmountDiscountStrategy(9000);
discountStrategyDAO.save(strategy);
DiscountRule rule = new SequenceRule(1);
discountRuleDAO.save(rule);
DiscountRule rule = new SequenceRule(1);
discountRuleDAO.save(rule);
41 / 객체지향적인 도메인 레이어 구축하기
42. Conclusion 임피던스 불일치의 결과
Anemic Domain Model & Fat Service Layer
public class Movie { public class ReservationService {
private Long id; public Money calculateFee(Movie movie,
private String title; DiscountStrategy strategy) {
if (strategy.isAmountType()) {
public Long getId() { ...
return id; } else if (strategy.isPercentType()) {
} ...
} else {
public String getTitle() { ...
return title; }
} ...
}
public class ReservationService {
public Reservation reserveShowing(int customerId,
int showingId, int audienceCount) {
Showing showing = showingDAO.selectShowing(showingId);
Movie movie = movieDAO.selectMovie(showing.getMovieId());
long amount = calculateFee(movie.getFee(), showing, audienceCount);
42 / 객체지향적인 도메인 레이어 구축하기
43. 아키텍처 패턴
User Interface
Service
Domain
Infrastructure
Transaction Script
43 / 객체지향적인 도메인 레이어 구축하기
45. DATA MAPPER
객체 모델과 DB 스키마 간의 독립성 유지
도메인 객체는 DB에 대해 독립적
ORMObject-Relational Mapper
RULE
ID
Rule
RuleMapper DISCOUNT_ID(FK)
POSITION
insert RULE_TYPE
update DAY_OF_WEEK
delete START_TIME
SequenceRule TimeOfDayRule
END_TUME
SEQUENCE
45 / 객체지향적인 도메인 레이어 구축하기
46. JPAJava Persistence API
Java ORM 표준 명세
Annotation과 XML을 이용한 META DATA MAPPING 기능 제공
Hibernate, EclipseLink
RULE
ID
Rule
JPA DISCOUNT_ID(FK)
POSITION
Hibernate RULE_TYPE
DAY_OF_WEEK
EclipseLink START_TIME
SequenceRule TimeOfDayRule
END_TUME
SEQUENCE
46 / 객체지향적인 도메인 레이어 구축하기
47. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
47 / 객체지향적인 도메인 레이어 구축하기
48. Mismatch 구성 요소 크기Granularity의 불일치
MOVIE
ID(PK)
TITLE
RUNNING_TIME
Movie FEE_AMOUNT
FEE_CURRENCY
제목
하나와 앨리스
Value Object
시간
135분
Entity <<value object>>
running Duration
Time
요금 quantity
<<entity>>
8,000원
Movie
title <<value object>>
calculateFee(showing):Money
Money
amount
currency
fee
48 / 객체지향적인 도메인 레이어 구축하기
49. Solution @Embeddable
Value Object
Entity <<value object>> MOVIE
running Duration
Time ID(PK)
quantity
<<entity>>
Movie TITLE
RUNNING_TIME
title <<value object>>
FEE_AMOUNT
calculateFee(showing):Money
Money
FEE_CURRENCY
amount
currency
fee
@Embeddable
public class Duration {
@Column(name="RUNNING_TIME", nullable=true)
private long quantity;
@Entity @Embeddable
@Table(name="MOVIE") public class Money {
public class Movie { @Column(name="FEE_AMOUNT", nullable=true)
private Duration runningTime; private BigDecimal amount;
private Money fee;
@Column(name="FEE_CURRENCY", nullable=true)
private Currency currency;
49 / 객체지향적인 도메인 레이어 구축하기
50. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
50 / 객체지향적인 도메인 레이어 구축하기
51. Mismatch 식별자 불일치Identity Mismatch
MOVIE
ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY
1 시간을 달리는 소녀 95 8,000 KRW
2 시간을 달리는 소녀 95 8,000 KRW
movie1 : Movie movie2 : Movie
title = ‚시간을 달리는 소녀‛ title = ‚시간을 달리는 소녀‛
runningTime = 95 runningTime = 95
Fee = 8000 Fee = 8000
51 / 객체지향적인 도메인 레이어 구축하기
52. Solution 영속 컨텍스트Persistence Context
Begin Commit/
User Interface TX Rollback
UNIT OF WORK
Service
IDENTITY
MAP
Domain
Infrastructure
52 / 객체지향적인 도메인 레이어 구축하기
53. 영화 엔티티Entity 조회
MOVIE
ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY
1 시간을 달리는 소녀 95 8,000 KRW
2 시간을 달리는 소녀 95 8,000 KRW
movie1 : Movie movie2 : Movie
title = ‚시간을 달리는 소녀‛ title = ‚시간을 달리는 소녀‛
runningTime = 95 runningTime = 95
Fee = 8000 Fee = 8000
53 / 객체지향적인 도메인 레이어 구축하기
54. 영속 컨텍스트Persistence Context
Begin Commit/
User Interface TX Rollback
UNIT OF WORK
Service
IDENTITY
MAP
movie1 : Movie
title = ‚시간을 달리는 소녀‛
ID 1 runningTime = 95
Fee = 8000
Domain
movie2 : Movie
ID 2 title = ‚시간을 달리는 소녀‛
runningTime = 95
Fee = 8000
Infrastructure
54 / 객체지향적인 도메인 레이어 구축하기
55. 첫 번째 영화 엔티티 다시 조회
MOVIE
ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY
1 시간을 달리는 소녀 95 8,000 KRW
2 시간을 달리는 소녀 95 8,000 KRW
movie1 : Movie movie2 : Movie
title = ‚시간을 달리는 소녀‛ title = ‚시간을 달리는 소녀‛
runningTime = 95 runningTime = 95
Fee = 8000 Fee = 8000
55 / 객체지향적인 도메인 레이어 구축하기
56. 영속 컨텍스트Persistence Context
Begin Commit/
User Interface TX Rollback
UNIT OF WORK
Service
IDENTITY
MAP
movie1 : Movie
title = ‚시간을 달리는 소녀‛
ID 1 runningTime = 95
Fee = 8000
Domain
movie2 : Movie
ID 2 title = ‚시간을 달리는 소녀‛
runningTime = 95
Fee = 8000
Infrastructure
56 / 객체지향적인 도메인 레이어 구축하기
57. 식별자 일관성 보장
데이터베이스 주키Primary Key와 객체 식별자Identity 간 일관성 유지
MOVIE
ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY
1 시간을 달리는 소녀 95 8,000 KRW
2 시간을 달리는 소녀 95 8,000 KRW
movie1 : Movie movie2 : Movie
title = ‚시간을 달리는 소녀‛ title = ‚시간을 달리는 소녀‛
runningTime = 95 runningTime = 95
Fee = 8000 Fee = 8000
57 / 객체지향적인 도메인 레이어 구축하기
59. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
59 / 객체지향적인 도메인 레이어 구축하기
60. Mismatch 다형성Polymorphism
{abstract}
Movie
DiscountStrategy
calculateFee(showing) registerDate Movie DiscountStrategy
calculateFee(showing)
id movieId
title discountType
runningTime amount
fee percent
Movie Discount feeCurrency registerDate
Amount Percent calculateFee() Non
calculateFee()
DiscountStrategy DiscountStrategy DiscountStrategy
Amount Discount 800원 discountAmount percent
Percent Discount 10%
public class ReservationService {
public Money calculateFee(Movie movie,
DiscountStrategy discountStrategy) {
if (discountStrategy.isAmountType()) {
...
No Discount 0원(%)
} else if (discountStrategy.isPercentType()) {
...
} else {
...
}
...
}
}
60 / 객체지향적인 도메인 레이어 구축하기
61. Solution 상속 매핑Inheritance Mapping
{abstract}
DiscountStrategy
registerDate
calculateFee(showing)
Amount Percent
DiscountStrategy DiscountStrategy
discountAmount percent
Single Table Concrete Class Class Table
Inheritance Inheritance Inheritance
DISCOUNT AMOUNT_DISCOUNT DISCOUNT
MOVIE_ID(FK) MOVIE_ID(FK)
MOVIE_ID(FK)
DISCOUNT_TYPE
FEE_AMOUNT REGISTER_DATE
FEE_AMOUNT
FEE_CURRENCY
FEE_CURRENCY
REGISTER_DATE
PERCENT
REGISTER_DATE
PERCENT_DISCOUNT AMOUNT_DISCOUN PERCENT_DISCOUN
T T
MOVIE_ID(FK) DISCOUNT_ID(FK) DISCOUNT_ID(FK)
PERCENT FEE_AMOUNT PERCENT
REGISTER_DATE FEE_CURRENCY
61 / 객체지향적인 도메인 레이어 구축하기
62. Solution 다형성을 통한 조건 분기 제거
{abstract}
Movie DiscountStrategy
Movie Discount calculateFee(showing) registerDate
calculateFee(showing)
Amount Discount 800원
Amount Percent Non
DiscountStrategy DiscountStrategy DiscountStrategy
Percent Discount 10%
discountAmount percent
public class Movie {
No Discount 0원(%) private DiscountStrategy discountStrategy;
public Money calculateFee(Showing showing) {
return discountStrategy.calculateFee(showing);
}
}
62 / 객체지향적인 도메인 레이어 구축하기
63. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
63 / 객체지향적인 도메인 레이어 구축하기
64. Mismatch 연관관계Association와 외래키Foreign Key
MOVIE SHOWING
ID(PK) ID(PK)
TITLE MOVIE_ID(FK)
RUNNING_TIME SEQUENCE
FEE_AMOUNT SHOWING_TIME
FEE_CURRENCY
Movie Showing
showing.getMovie();
calculateFee(showing) 1 * reserve(customer, count)
Movie Showing
movie.getShowings();
calculateFee(showing) 1 * reserve(customer, count)
Movie Showing movie.getShowings();
calculateFee(showing) 1 * reserve(customer, count) showing.getMovie();
64 / 객체지향적인 도메인 레이어 구축하기
65. Solution 외래키 맵핑Foreign Key Mapping
MOVIE SHOWING
ID(PK) ID(PK)
TITLE MOVIE_ID(FK)
RUNNING_TIME SEQUENCE
FEE_AMOUNT SHOWING_TIME
FEE_CURRENCY
Foreign Key
public class Showing {
@ManyToOne(optional=false)
@JoinColumn(name="MOVIE_ID")
private Movie movie;
public Money calculateFee() {
return movie.calculateFee(this);
}
}
Association
Movie Showing
calculateFee(showing) 1 * reserve(customer, count)
65 / 객체지향적인 도메인 레이어 구축하기
66. 임피던스 불일치 유형
구성 요소 크기Granularity의 불일치
식별자Identity의 불일치
다형성Polymorphism의 불일치
연관 관계Association와 외래키Foreign Key의 불일치
캡슐화 경계Encapsulation Boundary의 불일치
66 / 객체지향적인 도메인 레이어 구축하기
67. Mismatch 캡슐화Encapsulation
불변식 위반Invariant Violation
Movie movie = movieDAO.selectMovie(1);
Hierarchical Flat movie.setFee(8000);
Showing
reserve(customer, count)
DiscountStrategy strategy =
new AmountDiscountStrategy(9000);
Movie
discountStrategyDAO.save(strategy);
calculateFee(showing):Money
{abstract}
DiscountStrategy
calculateFee(showing):Mon
ey
{abstract}
Rule DiscountRule rule = new SequenceRule(1);
isStatisfiedBy(sho
wing) discountRuleDAO.save(rule);
Amount Percent NonDiscoun
t
Strategy Strategy
Strategy TimeOf
Sequence Day
Rule
Rule
DiscountRule rule = new SequenceRule(1);
discountRuleDAO.save(rule);
67 / 객체지향적인 도메인 레이어 구축하기
68. Solution 페치 젂략Fetch Strategy
지연 페치Lazy Fetch
public class Movie {
@OneToOne(cascade=CascadeType.ALL, optional=true, fetch=FetchType.LAZY)
private DiscountStrategy discountStrategy;
public class DiscountStrategy {
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY)
@JoinColumn(name="DISCOUNTID")
private Set<Rule> rules = new HashSet<Rule>();
{abstract} {abstract}
Showing Movie DiscountStrategy Rule
Proxy Proxy
1 * calculateFee(showing) 1 0..1 calculateFee(showing) 1 0..* calculateFee(showing)
reserve(customer, count)
68 / 객체지향적인 도메인 레이어 구축하기
69. Solution 페치 젂략Fetch Strategy
선행 페치Eager Fetch
public class Movie {
@OneToOne(cascade=CascadeType.ALL, optional=true, fetch=FetchType.EAGER)
private DiscountStrategy discountStrategy;
public class DiscountStrategy {
@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
@JoinColumn(name="DISCOUNTID")
private Set<Rule> rules = new HashSet<Rule>();
{abstract} {abstract}
Showing Movie DiscountStrategy Rule
reserve(customer, count) 1 * calculateFee(showing) 1 0..1 calculateFee(showing) 1 0..* calculateFee(showing)
69 / 객체지향적인 도메인 레이어 구축하기
70. Solution Aggregate = 캡슐화 경계Encapsulation Boundary
예기치 못한 변경으로부터 보호
객체 그룹의 불변성 보장
Showing
reserve(customer, count)
Aggregate
Movie
{abstract}
calculateFee(showing):Money DiscountStrategy
calculateFee(showing):Money
{abstract}
Rule
isStatisfiedBy(showing)
Amount Percent NonDiscount
Strategy Strategy Strategy
Sequence TimeOfDay
Rule Rule
Eager Fetch
Lazy Fetch
70 / 객체지향적인 도메인 레이어 구축하기
77. 타협하라
Be Pragmatic
데이터베이스가 하나의 객체 저장소로 보여진다면 매핑 도구의 기능과는 상관없이 데이터
모델과 객체 모델이 서로 갈라지게 해서는 안 된다. 일부 객체 관계의 풍부함을 희생해서
관계 모델에 밀접하게 한다. 객체 매핑을 단순화하는데 도움이 된다면 정규화와 같은 정형
화된 관계 표준을 젃충한다.
- Eric Evans
77 / 객체지향적인 도메인 레이어 구축하기
82. 예매 생성 책임
예매 생성에 필요한 정보의 EXPERT에게 할당Creator
Showing
상영 정보를 알고 있다
예매 정보를 생성한다
82 / 객체지향적인 도메인 레이어 구축하기
83. 가격 계산 책임
영화 가격 정보를 알고 있는 EXPERT에 할당Information Expert
Movie
Showing 영화 정보를 알고 있다
가격을 계산한다
상영 정보를 알고 있다 Movie
예매 정보를 생성한다
83 / 객체지향적인 도메인 레이어 구축하기
84. 할인율 계산 책임
할인율을 적용할 STRATEGY 객체 추가
Movie
Showing 영화 정보를 알고 있다 Discount
Strategy
가격을 계산한다
상영 정보를 알고 있다 Movie
예매 정보를 생성한다 DiscountStrategy
할인율 정책을 알고 있다
할인된 가격을 계산한다
84 / 객체지향적인 도메인 레이어 구축하기
85. 할인 여부를 판단할 책임
할인 정책을 판단하기 위한 SPECIFICATION 객체 추가
Movie
Showing 영화 정보를 알고 있다 Discount
Strategy
가격을 계산한다
상영 정보를 알고 있다 Movie
예매 정보를 생성한다 DiscountStrategy
할인율 정책을 알고 있다 Rule
할인된 가격을 계산한다
Rule
할인 정책을 알고 있다 Showing
할인 여부를 판단한다
85 / 객체지향적인 도메인 레이어 구축하기
86. Appendix B.
Domain Model Pattern Implementation
87. Domain Layer Collaboration
public class Showing {
public Reservation reserve(Customer customer, int audienceCount) {
return new Reservation(customer, this, audienceCount);
}
}
public class Reservation {
public Reservation(Customer customer, Showing showing, int audienceCount) {
this.customer = customer;
this.showing = showing;
this.fee = showing.calculateFee().times(audienceCount);
this.audienceCount = audienceCount;
}
} public class Showing {
public Money calculateFee() {
return movie.calculateFee(this);
}
}
public class Movie {
public Money calculateFee(Showing showing) {
return discountStrategy.calculateFee(showing);
}
}
87 / 객체지향적인 도메인 레이어 구축하기
88. Domain Layer Collaboration
public abstract class DiscountStrategy {
public Money calculateFee(Showing showing) {
for(Rule each : rules) {
if (each.isStatisfiedBy(showing)) {
return getDiscountedFee(showing);
}
}
return showing.getFixedFee();
}
abstract protected Money getDiscountedFee(Showing showing);
public abstract class Rule {
abstract public boolean isStatisfiedBy(Showing showing);
}
public class SequenceRule extends Rule {
public boolean isStatisfiedBy(Showing showing) {
return showing.isSequence(sequence);
}
}
public class TimeOfDayRule extends Rule {
public boolean isStatisfiedBy(Showing showing) {
return showing.isPlayingOn(dayOfWeek) &&
Interval.closed(startTime, endTime)
.includes(showing.getPlayngInterval());
}
}
88 / 객체지향적인 도메인 레이어 구축하기
89. Domain Layer Collaboration
public abstract class DiscountStrategy {
public Money calculateFee(Showing showing) {
for(Rule each : rules) {
if (each.isStatisfiedBy(showing)) {
return getDiscountedFee(showing);
}
}
return showing.getFixedFee();
}
abstract protected Money getDiscountedFee(Showing showing);
public class AmountDiscountStrategy extends DiscountStrategy {
protected Money getDiscountedFee(Showing showing) {
return showing.getFixedFee().minus(discountAmount);
}
}
public class NonDiscountStrategy extends DiscountStrategy {
protected Money getDiscountedFee(Showing showing) {
return showing.getFixedFee();
}
}
public class PercentDiscountStrategy extends DiscountStrategy {
protected Money getDiscountedFee(Showing showing) {
return showing.getFixedFee().minus(showing.getFixedFee().times(percent));
}
}
89 / 객체지향적인 도메인 레이어 구축하기
91. Data Model
RESERVATION CUSTOMER
ID ID
CUSTOMER_ID(FK) CUSTOMER_ID
SHOWING_ID(FK) NAME
FEE_AMOUNT
FEE_CURRENCY
AUDIENCE_COUNT
RULE
SHOWING MOVIE DISCOUNT
ID
ID ID MOVIE_ID(FK)
DISCOUNT_ID(FK)
MOVIE_ID(FK) TITLE DISCOUNT_TYPE POSITION
SEQUENCE RUNNING_TIME FEE_AMOUNT RULE_TYPE
SHOWING_TIME FEE_AMOUNT FEE_CURRENCY DAY_OF_WEEK
FEE_CURRENCY PERCENT START_TIME
REGISTER_DATE END_TUME
SEQUENCE
91 / 객체지향적인 도메인 레이어 구축하기