DOMAIN MODEL 패턴과 JPA 의조화객체지향적인도메인레이어구축하기 조영호 Eternity s Chit-Chat(http://aeternum.egloos.com)
목차 1. 온라인영화예매시스템도메인 2. 임피던스불일치Impedance Mismatch 3. JPA Java Persistence API 4. 결롞
1. 온라인영화예매시스템도메인
Domain Concept - 영화 4 / 객체지향적인도메인레이어구축하기
Domain Concept - 상영 2011-10-18 09:30 조조 Showing 2011-12-01 14:20 4 회 2011-10-21 20:30 5 회 5 / 객체지향적인도메인레이어구축하기
Domain Concept 할인정책 Discount Amount Discount \8,000 - \800 = \7,200 Percent Discount \8,000 (\8,000 * 0.1) = \7,200 6 / 객체지향적인도메인레이어구축하기
Domain Concept 할인규칙 Rule Sequence Rule 조조상영인경우 10 회상영인경우 Time Rule 월요일 10:00 ~ 12:00 상영인경우 화요일 18:00 ~ 21:00 상영인경우 7 / 객체지향적인도메인레이어구축하기
Domain Concept 할인정책 + 할인규칙 조조상영인경우 도가니 8000 원 Amount DC 800 원 10 회상영인경우 월요일 10:00 ~ 12:00 상영인경우 화요일 18:00 ~ 21:00 상영인경우 Discount Rule 1 0..1 1 1..* 8 / 객체지향적인도메인레이어구축하기
Domain Concept 예매 Reservation 제 목 도가니 상영정보 2011 년 10 월 18 일 ( 목 ) 7 회 6:00( 오후 ) 8:00( 오후 ) 인 원 2 명 정 가 16,000 원 결재금액 14,400 원 9 / 객체지향적인도메인레이어구축하기
Domain Model Showing Reservation 1 0..* 0..* 1 1 0..1 1 1..* Discount Rule 10 / 객체지향적인도메인레이어구축하기
영화예매책임수행을위한협력의설계 후보객체 Candidate, 책임 Responsibility, 협력 Collaboration 식별 Showing 상영정보를알고있다 예매정보를생성한다 영화정보를알고있다 가격을계산한다 Discount Strategy DiscountStrategy 할인율정책을알고있다 할인된가격을계산한다 Rule 할인정책을알고있다 할인여부를판단한다 Rule Showing 11 / 객체지향적인도메인레이어구축하기
도메인레이어객체구현 User Interface Service Reservation Domain Showing reserve(customer, count):reservation <<create>> Infrastructure Customer calculatefee(showing):money {abstract DiscountStrategy calculatefee(showing):money {abstract Rule isstatisfiedby(showing):boolean Amount Strategy Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule 12 / 객체지향적인도메인레이어구축하기
Domain Model Showing Reservation 1 0..* 0..* 1 1 0..1 1 1..* Discount Rule 13 / 객체지향적인도메인레이어구축하기
Domain Layer Design & Domain Model Showing Reservation Reservation Showing reserve(customer, count):reservation 1 0..* <<create>> 0..* Customer 1 calculatefee(showing):money {abstract DiscountStrategy calculatefee(showing):money {abstract Rule isstatisfiedby(showing):boolean Amount Strategy 1 0..1 1 1..* Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule Discount Rule 14 / 객체지향적인도메인레이어구축하기
아키텍처패턴 User Interface Service Domain Infrastructure Domain Model 15 / 객체지향적인도메인레이어구축하기
2. 임피던스불일치Impedance Mismatch
임피던스불일치Impedance Mismatch 17 / 객체지향적인도메인레이어구축하기
객체 - 관계임피던스불일치 객체모델과 DB 스키마간의불일치 객체패러다임 Object Paradigm 과관계패러다임 Relational Paradigm 간의불일치 RULE DiscountStrategy Rule DISCOUNT ID MOVIE_ID(FK) DISCOUNT_ID(FK) DISCOUNT_TYPE POSITION FEE_AMOUNT RULE_TYPE FEE_CURRENCY DAY_OF_WEEK Amount Strategy Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule PERCENT START_TIME END_TUME SEQUENCE 18 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 19 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 20 / 객체지향적인도메인레이어구축하기
Mismatch 구성요소크기 Granularity 의불일치 MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 제목 하나와앨리스 시간 135분요금 8,000원 title <<entity>> calculatefee(showing):money running Time fee <<value object>> Duration quantity <<value object>> Money amount currency 21 / 객체지향적인도메인레이어구축하기
Entity & Value Object Identity Value Value Object Entity <<entity>> running Time quantity <<value object>> Duration title calculatefee(showing):money fee amount currency <<value object>> Money 22 / 객체지향적인도메인레이어구축하기
책임의재사용 Money 영화정보를알고있다 가격을계산한다 금액과환율을알고있다 금액을 +,-,*,/ 한다 Duration 기간을알고있다. 기간의합, 차를계산한다 23 / 객체지향적인도메인레이어구축하기
Problem 코드중복Code Duplication MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY RESERVATION ID(PK) CUSTOMER_ID(FK) SHOWING_ID(FK) FEE_AMOUNT FEE_CURRENCY AUDIENCE_COUNT class { long plus(long fee, Currency currency) { if (this.feecurrency.equals(currency) { return this.fee + fee; id title runningtime fee feecurrency calculatefee() throw new IllegalArgumentException(); Reservation id customerid showingid amount acmointcurrency audiencecount class Reservation { long add(long amount, Currency currency) { if (this.amountcurrency.equals(currency) { return this.amount + fee; getfee() throw new IllegalArgumentException(); 24 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 25 / 객체지향적인도메인레이어구축하기
Mismatch 데이터베이스식별자Database Identity 테이블주키Primary Key 제목 시간을달리는소녀 시간 95 분 요금 8,000 원 제목 시간을달리는소녀 시간 95 분 요금 8,000 원 MOVIE ID 1 2 TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 시간을달리는소녀 95 8,000 KRW 시간을달리는소녀 95 8,000 KRW 26 / 객체지향적인도메인레이어구축하기
객체식별자Object Identity Java == 연산자 제목 시간을달리는소녀 시간 95 분 요금 8,000 원 제목 시간을달리는소녀 시간 95 분 요금 8,000 원 address 1 address 2 movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ 27 / 객체지향적인도메인레이어구축하기
Problem 데이터베이스-객체식별자불일치Identity Mismatch MOVIE ID 1 2 TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 시간을달리는소녀 95 8,000 KRW 시간을달리는소녀 95 8,000 KRW movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ 28 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 29 / 객체지향적인도메인레이어구축하기
Mismatch 다형성 Polymorphism Discount calculatefee(showing) {abstract DiscountStrategy registerdate calculatefee(showing) Amount Discount 800 원 Percent Discount 10% Amount DiscountStrategy Percent DiscountStrategy Non DiscountStrategy discountamount percent No Discount 0 원 (%) public class { private DiscountStrategy discountstrategy; public Money calculatefee(showing showing) { return discountstrategy.calculatefee(showing); 30 / 객체지향적인도메인레이어구축하기
Problem 젃차적인조건분기Conditional Branch DiscountStrategy Discount Amount Discount 800 원 id title runningtime fee feecurrency movieid discounttype amount percent registerdate calculatefee() calculatefee() Percent Discount 10% 31 / 객체지향적인도메인레이어구축하기 No Discount 0 원 (%) public class ReservationService { public class Money calculatefee( { movie, private DiscountStrategy discountstrategy) discountstrategy; { if (discountstrategy.isamounttype()) { public... Money calculatefee(showing showing) { return else discountstrategy.calculatefee(showing); if (discountstrategy.ispercenttype()) {... else {......
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 32 / 객체지향적인도메인레이어구축하기
Mismatch 데이터베이스외래키Foreign Key MOVIE SHOWING ID(PK) ID(PK) Showing TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY MOVIE_ID(FK) SEQUENCE SHOWING_TIME 1 회 10/18 10:00 2 회 10/18 12:30 MOVIE 3 회 10/18 15:00 ID 1 TITLE 하나와앨리스 4 회 10/18 17:30 SHOWING ID MOVIE_ID SEQUENCE SHOWING_TIME 1 1 7 2010-12-23 18:00 33 / 객체지향적인도메인레이어구축하기
테이블외래키 Foreign Key - 양방향 BiDirectional MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY SHOWING ID(PK) MOVIE_ID(FK) SEQUENCE SHOWING_TIME SELECT * FROM MOVIE as m LEFT JOIN SHOWING as s ON m.id = s.movie_id 34 / 객체지향적인도메인레이어구축하기
객체연관관계 Association 단방향 UniDirectional MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY SHOWING ID(PK) MOVIE_ID(FK) SEQUENCE SHOWING_TIME calculatefee(showing) 1 * Showing reserve(customer, count) showing.get(); calculatefee(showing) 1 * Showing reserve(customer, count) movie.getshowings(); calculatefee(showing) 1 * Showing reserve(customer, count) movie.getshowings(); showing.get(); 35 / 객체지향적인도메인레이어구축하기
객체협력 Collaboration 을통한구현 연관관계는책임분배와협력을위한핵심메커니즘 Showing 상영정보를알고있다 예매정보를생성한다 영화정보를알고있다 가격을계산한다 Discount Strategy DiscountStrategy 할인율정책을알고있다 할인된가격을계산한다 Rule reserve() Showing * 1 calculatefee() 1 0..1 DiscountStrategy calculatefee() public class Showing { private movie; public Money calculatefee() { return movie.calculatefee(this);... public class { private DiscountStrategy discountstrategy; public Money calculatefee(showing showing) { return discountstrategy.calculatefee(showing);... 36 / 객체지향적인도메인레이어구축하기
Problem Data + Process = 젃차적인 Procedural 프로그래밍 MOVIE SHOWING ID(PK) ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY MOVIE_ID(FK) SEQUENCE SHOWING_TIME Process Showing Data ReservationService reserveshowing() id title runningtime fee feecurrency id movieid title sequence showingtime public class ReservationService { public Reservation reserveshowing(int customerid, int showingid, int audiencecount) { public Showing class showing Showing = showingdao.selectshowing(showingid); { movie = moviedao.select(showing.getid()); private 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 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 38 / 객체지향적인도메인레이어구축하기
Mismatch 캡슐화 Encapsulation Hierarchical Flat Showing reserve(customer, count) calculatefee(showing):money {abstract DiscountStrategy calculatefee(showing):money {abstract Rule isstatisfiedby(showing) Amount Strategy Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule 39 / 객체지향적인도메인레이어구축하기
할인규칙등록 Showing {abstract DiscountStrategy {abstract Rule reserve(customer, count) 1 * calculatefee(showing) 1 0..1 calculatefee(showing) 1 0..* calculatefee(showing) 제목무지개여싞 상영시간 116 분요금 8,000 원 할인정책 AMOUNT DISCOUNT 할인금액 9,000 원 할인규칙 SEQUENCE RULE 할인대상 1 회 1 회 5 회 7 회 40 / 객체지향적인도메인레이어구축하기
할인규칙등록 Showing {abstract DiscountStrategy {abstract Rule reserve(customer, count) 1 * calculatefee(showing) 1 0..1 calculatefee(showing) 1 0..* calculatefee(showing) 캡슐화경계 Boundary 제목무지개여싞 상영시간 116 분요금 8,000 원 할인정책 AMOUNT DISCOUNT 할인금액 9,000 원 Discount 할인규칙 할인대상 1 회 1 회 SEQUENCE RULE 5 회 7 회 Rule 41 / 객체지향적인도메인레이어구축하기
Problem 캡슐화저해 Flat 불변식위반Invariant Violation movie.setfee(8000); moviedao.save(movie); DiscountStrategy strategy = new AmountDiscountStrategy(9000); discountstrategydao.save(strategy); DiscountRule rule = new SequenceRule(1); discountruledao.save(rule); DiscountRule rule = new SequenceRule(1); discountruledao.save(rule); 42 / 객체지향적인도메인레이어구축하기
Conclusion 임피던스불일치의결과 Anemic Domain Model & Fat Service Layer public class { private Long id; private String title; public Long getid() { return id; public String gettitle() { return title; public class ReservationService { public Money calculatefee( movie, DiscountStrategy strategy) { if (strategy.isamounttype()) {... else if (strategy.ispercenttype()) {... else {...... public class ReservationService { public Reservation reserveshowing(int customerid, int showingid, int audiencecount) { Showing showing = showingdao.selectshowing(showingid); movie = moviedao.select(showing.getid()); long amount = calculatefee(movie.getfee(), showing, audiencecount); 43 / 객체지향적인도메인레이어구축하기
아키텍처패턴 User Interface Service Domain Infrastructure Transaction Script 44 / 객체지향적인도메인레이어구축하기
3. JPA Java Persistence API
DATA MAPPER 객체모델과 DB 스키마간의독립성유지 도메인객체는 DB 에대해독립적 ORMObject-Relational Mapper RULE Rule ID RuleMapper DISCOUNT_ID(FK) SequenceRule TimeOfDayRule insert update delete POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE 46 / 객체지향적인도메인레이어구축하기
JPA Java Persistence API Java ORM 표준명세 Annotation 과 XML 을이용한 META DATA MAPPING 기능제공 Hibernate, EclipseLink RULE Rule ID SequenceRule TimeOfDayRule JPA Hibernate EclipseLink DISCOUNT_ID(FK) POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE 47 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 48 / 객체지향적인도메인레이어구축하기
Mismatch 구성요소크기 Granularity 의불일치 MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 제목 하나와앨리스 시간 Value Object 135분요금 Entity running Time <<value object>> Duration quantity 8,000 원 <<entity>> title calculatefee(showing):money fee <<value object>> Money amount currency 49 / 객체지향적인도메인레이어구축하기
Solution @Embeddable Value Object Entity running Time <<value object>> Duration quantity MOVIE ID(PK) title <<entity>> calculatefee(showing):money <<value object>> Money amount TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY fee currency @Embeddable public class Duration { @Column(name="RUNNING_TIME", nullable=true) private long quantity; @Entity @Table(name="MOVIE") public class { private Duration runningtime; private Money fee; @Embeddable public class Money { @Column(name="FEE_AMOUNT", nullable=true) private BigDecimal amount; @Column(name="FEE_CURRENCY", nullable=true) private Currency currency; 50 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 51 / 객체지향적인도메인레이어구축하기
Mismatch 식별자불일치Identity Mismatch MOVIE ID 1 2 TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 시간을달리는소녀 95 8,000 KRW 시간을달리는소녀 95 8,000 KRW movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ 52 / 객체지향적인도메인레이어구축하기
Solution 영속컨텍스트Persistence Context User Interface UNIT OF WORK Begin TX Commit/ Rollback Service IDENTITY MAP Domain Infrastructure 53 / 객체지향적인도메인레이어구축하기
영화엔티티 Entity 조회 MOVIE ID 1 2 TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 시간을달리는소녀 95 8,000 KRW 시간을달리는소녀 95 8,000 KRW movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ 54 / 객체지향적인도메인레이어구축하기
영속컨텍스트Persistence Context User Interface UNIT OF WORK Begin TX Commit/ Rollback Service Domain IDENTITY MAP ID 1 ID 2 movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ Infrastructure 55 / 객체지향적인도메인레이어구축하기
첫번째영화엔티티다시조회 MOVIE ID 1 2 TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 시간을달리는소녀 95 8,000 KRW 시간을달리는소녀 95 8,000 KRW movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ 56 / 객체지향적인도메인레이어구축하기
영속컨텍스트 Persistence Context User Interface UNIT OF WORK Begin TX Commit/ Rollback Service Domain IDENTITY MAP ID 1 ID 2 movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ Infrastructure 57 / 객체지향적인도메인레이어구축하기
식별자일관성보장 데이터베이스주키 Primary Key 와객체식별자 Identity 간일관성유지 MOVIE ID 1 2 TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 시간을달리는소녀 95 8,000 KRW 시간을달리는소녀 95 8,000 KRW movie1 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ movie2 : title = 시간을달리는소녀 runningtime = 95 Fee = 8000\ 58 / 객체지향적인도메인레이어구축하기
식별자일관성보장 movie1 = entitymanager.load(.class, 1); movie2 = entitymanager.load(.class, 1); assertsame(movie1, movie2); 59 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 60 / 객체지향적인도메인레이어구축하기
Mismatch 다형성 Polymorphism {abstract DiscountStrategy calculatefee(showing) registerdate DiscountStrategy Discount calculatefee(showing) id title runningtime fee feecurrency movieid discounttype amount percent registerdate Amount DiscountStrategy Percent calculatefee() DiscountStrategy Non DiscountStrategy calculatefee() Amount Discount 800 원 discountamount percent Percent Discount 10% No Discount 0 원 (%) public class ReservationService { public Money calculatefee( movie, DiscountStrategy discountstrategy) { if (discountstrategy.isamounttype()) {... else if (discountstrategy.ispercenttype()) {... else {...... 61 / 객체지향적인도메인레이어구축하기
Solution 상속매핑Inheritance Mapping {abstract DiscountStrategy registerdate calculatefee(showing) Amount DiscountStrategy discountamount Percent DiscountStrategy percent Single Table Inheritance DISCOUNT MOVIE_ID(FK) DISCOUNT_TYPE FEE_AMOUNT FEE_CURRENCY PERCENT REGISTER_DATE Concrete Class Inheritance AMOUNT_DISCOUNT MOVIE_ID(FK) FEE_AMOUNT FEE_CURRENCY REGISTER_DATE PERCENT_DISCOUNT MOVIE_ID(FK) PERCENT REGISTER_DATE Class Table Inheritance DISCOUNT AMOUNT_DISCOUN T DISCOUNT_ID(FK) FEE_AMOUNT FEE_CURRENCY MOVIE_ID(FK) REGISTER_DATE PERCENT_DISCOUN T DISCOUNT_ID(FK) PERCENT 62 / 객체지향적인도메인레이어구축하기
Solution 다형성을통한조건분기제거 Discount calculatefee(showing) {abstract DiscountStrategy registerdate calculatefee(showing) Amount Discount 800 원 Percent Discount 10% Amount DiscountStrategy Percent DiscountStrategy Non DiscountStrategy discountamount percent No Discount 0 원 (%) public class { private DiscountStrategy discountstrategy; public Money calculatefee(showing showing) { return discountstrategy.calculatefee(showing); 63 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 64 / 객체지향적인도메인레이어구축하기
Mismatch 연관관계 Association 와외래키Foreign Key MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY SHOWING ID(PK) MOVIE_ID(FK) SEQUENCE SHOWING_TIME calculatefee(showing) 1 * Showing reserve(customer, count) showing.get(); calculatefee(showing) 1 * Showing reserve(customer, count) movie.getshowings(); calculatefee(showing) 1 * Showing reserve(customer, count) movie.getshowings(); showing.get(); 65 / 객체지향적인도메인레이어구축하기
Solution 외래키맵핑Foreign Key Mapping MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY SHOWING ID(PK) MOVIE_ID(FK) SEQUENCE SHOWING_TIME Foreign Key public class Showing { @ManyToOne(optional=false) @JoinColumn(name="MOVIE_ID") private movie; public Money calculatefee() { return movie.calculatefee(this); Association calculatefee(showing) 1 * Showing reserve(customer, count) 66 / 객체지향적인도메인레이어구축하기
임피던스불일치유형 구성요소크기 Granularity 의불일치 식별자 Identity 의불일치 다형성 Polymorphism 의불일치 연관관계 Association 와외래키 Foreign Key 의불일치 캡슐화경계 Encapsulation Boundary 의불일치 67 / 객체지향적인도메인레이어구축하기
Mismatch 캡슐화 Encapsulation 불변식위반Invariant Violation Hierarchical Flat movie.setfee(8000); moviedao.save(movie); Amount Strategy Showing reserve(customer, count) calculatefee(showing):money {abstract DiscountStrategy calculatefee(showing):mon ey {abstract Rule isstatisfiedby(sho wing) Percent Strategy NonDiscoun t Strategy Sequence Rule TimeOf Day Rule DiscountStrategy strategy = new AmountDiscountStrategy(9000); discountstrategydao.save(strategy); DiscountRule rule = new SequenceRule(1); discountruledao.save(rule); DiscountRule rule = new SequenceRule(1); discountruledao.save(rule); 68 / 객체지향적인도메인레이어구축하기
Solution 페치젂략Fetch Strategy 지연페치Lazy Fetch public class { @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>(); Showing reserve(customer, count) 1 * calculatefee(showing) 1 0..1 {abstract DiscountStrategy Proxy calculatefee(showing) 1 0..* {abstract Rule Proxy calculatefee(showing) 69 / 객체지향적인도메인레이어구축하기
Solution 페치젂략Fetch Strategy 선행페치Eager Fetch public class { @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>(); Showing {abstract DiscountStrategy {abstract Rule reserve(customer, count) 1 * calculatefee(showing) 1 0..1 calculatefee(showing) 1 0..* calculatefee(showing) 70 / 객체지향적인도메인레이어구축하기
Solution Aggregate = 캡슐화경계Encapsulation Boundary 예기치못한변경으로부터보호 객체그룹의불변성보장 Showing reserve(customer, count) Aggregate calculatefee(showing):money {abstract DiscountStrategy calculatefee(showing):money {abstract Rule isstatisfiedby(showing) Amount Strategy Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule Eager Fetch Lazy Fetch 71 / 객체지향적인도메인레이어구축하기
4. 결롞
DOMAIN MODEL PATTERN 도메인을구성하는개념들을따르는도메인레이어 적젃한책임의분배와협력을통한객체지향적인도메인레이어 73 / 객체지향적인도메인레이어구축하기
임피던스불일치 맹목적으로테이블의구조를따르는객체구조 젃차적인 TRANSACTION SCRIPT 로향하는한가지이유 74 / 객체지향적인도메인레이어구축하기
JPA JavaPersistece API 임피던스불일치를해결할수있는실용적인솔루션 풍부한도메인레이어 Rich Domain Layer 의기반 75 / 객체지향적인도메인레이어구축하기
Domain Model 에초점을맞춰라 Showing Reservation Reservation Showing reserve(customer, count):reservation 1 0..* <<create>> 0..* Customer 1 calculatefee(showing):money {abstract DiscountStrategy calculatefee(showing):money {abstract Rule isstatisfiedby(showing):boolean Amount Strategy 1 0..1 1 1..* Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule Discount Rule 76 / 객체지향적인도메인레이어구축하기
Data Model 이더중요 77 / 객체지향적인도메인레이어구축하기
타협하라 Be Pragmatic 데이터베이스가하나의객체저장소로보여진다면매핑도구의기능과는상관없이데이터모델과객체모델이서로갈라지게해서는안된다. 일부객체관계의풍부함을희생해서관계모델에밀접하게한다. 객체매핑을단순화하는데도움이된다면정규화와같은정형화된관계표준을젃충한다. - Eric Evans 78 / 객체지향적인도메인레이어구축하기
Thank you. 79 / 객체지향적인도메인레이어구축하기
Question? 80 / 객체지향적인도메인레이어구축하기
참고자료 - Christian Bauer, Gavin King, Java Persistence with Hibernate, Manning, 2006. - Michael Keith, Merrick Schincario, Pro JPA2 : Mastering the Java(TM) Persistence API, Apress, 2009. - Chris Richardson, POJOs in Action : Developing Enterprise Applications with Lightweight Frameworks, Manning, 2006. - Martin Fowler, Patterns of Enterprise Application Architecture, Addison-Wesley, 2002. - Eric Evans, Domain-Driven Design, Addison-Wesley, 2003. - Kent Beck and Ward Cunningham, A Laboratory for Teaching Object Oriented Thinking, OOPSLA 89 Conference Proceedings, SIGPLAN Notices, 24(10), pp. 1-6, New Orleans, Louisiana, October 1989. - Rebecca Wirfs-Brock and Brian Wilkerson, Object-Oriented Design: A Responsibility-Driven Approach, OOPSLA 89 Conference Proceedings, SIGPLAN Notices, 24(10), pp.71-76, New Orleans, Louisiana, October, 1989. - Rebecca Wirfs-Brock, Alan McKean, Object Design : Roles, Responsibilities, and Collaborations, Addison- Wesley, 2002. 81 / 객체지향적인도메인레이어구축하기
Appendix A. CRC Card
예매생성책임 예매생성에필요한정보의 EXPERT 에게할당 Creator Showing 상영정보를알고있다 예매정보를생성한다 83 / 객체지향적인도메인레이어구축하기
가격계산책임 영화가격정보를알고있는 EXPERT에할당Information Expert Showing 상영정보를알고있다 예매정보를생성한다 영화정보를알고있다 가격을계산한다 84 / 객체지향적인도메인레이어구축하기
할인율계산책임 할인율을적용할 STRATEGY 객체추가 Showing 상영정보를알고있다 예매정보를생성한다 영화정보를알고있다 가격을계산한다 Discount Strategy DiscountStrategy 할인율정책을알고있다 할인된가격을계산한다 85 / 객체지향적인도메인레이어구축하기
할인여부를판단할책임 할인정책을판단하기위한 SPECIFICATION 객체추가 Showing 상영정보를알고있다 예매정보를생성한다 영화정보를알고있다 가격을계산한다 Discount Strategy DiscountStrategy 할인율정책을알고있다 할인된가격을계산한다 Rule 할인정책을알고있다 할인여부를판단한다 Rule Showing 86 / 객체지향적인도메인레이어구축하기
Appendix B. Domain Model Pattern Implementation
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 { public Money calculatefee(showing showing) { return discountstrategy.calculatefee(showing); 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()); 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)); 90 / 객체지향적인도메인레이어구축하기
Domain Layer Collaboration :Showing :Reservation : :DiscountStrategy :Rule reserve() new calculatefee() calculatefee() calculatefee() * isstatisfied() 91 / 객체지향적인도메인레이어구축하기
Data Model RESERVATION CUSTOMER ID ID CUSTOMER_ID(FK) SHOWING_ID(FK) FEE_AMOUNT FEE_CURRENCY AUDIENCE_COUNT CUSTOMER_ID NAME SHOWING MOVIE DISCOUNT RULE ID ID MOVIE_ID(FK) SEQUENCE SHOWING_TIME ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY MOVIE_ID(FK) DISCOUNT_TYPE FEE_AMOUNT FEE_CURRENCY PERCENT REGISTER_DATE DISCOUNT_ID(FK) POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE 92 / 객체지향적인도메인레이어구축하기
Appendix C. Impedance Mismatch Summary
94 / 객체지향적인도메인레이어구축하기 Duration quantity Money amount currency running Time fee title MOVIE ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY * 1 Showing * 1 Showing * 1 Showing MOVIE SHOWING ID(PK) MOVIE_ID(FK) SEQUENCE SHOWING_TIME {abstract DiscountStrategy registerdate Percent Discount Strategy percent Amount Discount Strategy discountamount Non Discount Strategy DISCOUNT MOVIE_ID(FK) DISCOUNT_TYPE FEE_AMOUNT FEE_CURRENCY PERCENT REGISTER_DATE AMOUNT_ DISCOUNT MOVIE_ID(FK) FEE_AMOUNT FEE_CURRENCY REGISTER_DATE PERCENT_ DISCOUNT MOVIE_ID(FK) PERCENT REGISTER_DATE DISCOUNT MOVIE_ID(FK) REGISTER_DATE AMOUNT_ DISCOUNT DISCOUNT_ID(FK) FEE_AMOUNT FEE_CURRENCY PERCENT_ DISCOUNT DISCOUNT_ID(FK) PERCENT ID(PK) TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 임피던스불일치 Impedance Mismatch