애플리케이션 아키텍처와 객체지향
Eternity’s Chit-Chat
http://aeternum.egloos.com/
Object-Orientated
 Application
 Architecture
Part
 - 1
아키텍처Architecture
아키텍처Architecture
프로젝트에 참여하는 개발자들이
설계에 대해 공유하는 이해를 반영하는
주관적인 개념
아키텍처Architecture
서로 다르고 관련이 없는
책임responsibility들을 분리
관심사의 분리Separation
 of
 Concerns
레이어 아키텍처Layered
 Architecture
7 / 문서의 제목
Presentation
Domain
Data
 Source
레이어 아키텍처Layered
 Architecture
8 / 문서의 제목
Presentation
Data
 Source
가장 중요한 레이어는 도메인 레이어
Domain
9 / 문서의 제목
도메인 레이어를 설계하는 방법
Transaction Script Domain Model
10 / 문서의 제목
Presentation
Data
 Source
도메인 레이어가 전체 아키텍처 구성을 주도
Domain
11 / 문서의 제목
Part
 - 2
영화 예매 도메인
온라인 영화 예매 시스템
Domain
 Concept
 - 영화
Movie
Domain
 Concept
 - 상영
2010-10-20 09:30 조조
Showing
2010-10-21 20:30 5회
2010-12-01 14:20 4회
Domain
 Concept
 ­– 할인 정책
Discount Amount Discount
Percent Discount
8,000 - 800 = 7,200
8,000 – (8,000 * 0.1) = 7,200
Domain
 Concept
 ­– 할인 규칙
Rule Sequence Rule
Time Rule
조조 상영인 경우
월요일 10:00 ~ 12:00 상영인 경우
목요일 18:00 ~ 21:00 상영인 경우
10회 상영인 경우
Domain
 Concept
 ­–할인 정책 +
 할인 규칙
Movie Discount Rule
1 0..1 1 1..*
10회 상영인 경우
Domain
 Concept
 ­–할인 정책 +
 할인 규칙
Movie Discount Rule
1 0..1 1 1..*
이끼
8000원
Amount DC
800원
조조 상영인 경우
월요일 10:00 ~ 12:00 상영인 경우
목요일 18:00 ~ 21:00 상영인 경우
Domain
 Concept
 ­–할인 적용
상영정보
2010년 12월 23일
목요일
18:00 ~ 20:00(7회차)
10회 상영인 경우
이끼
8000원
Amount	
  DC
800원
조조 상영인 경우
월요일 10:00 ~ 12:00 상영인 경우
목요일 18:00 ~ 21:00 상영인 경우
Domain
 Concept
 ­–할인 적용
상영정보
2010년 12월 23일
목요일
18:00 ~ 20:00(7회차)
10회 상영인 경우
이끼
8000원
Amount DC
800원
조조 상영인 경우
월요일 10:00 ~ 12:00 상영인 경우
목요일 18:00 ~ 21:00 상영인 경우
7,200원
Domain
 Concept
 ­–예매
Reservation
이끼
2010년 12월 23일 (목)
7회 6:00(오후) – 8:00(오후)
2명
16,000원
14,400원
제 목
상영 정보
인 원
정 가
결재 금액
Movie Discount Rule
1 0..1 1 1..*
1
0..*
1 0..*
Showing Reservation
도메인(Domain) 개념 또는 후보 객체
Domain
Part
 - 3
트랜잭션 스크립트Transaction
 Script
Process Data
절차적Procedural
무엇을 저장할 것인가 - 데이터
데이터 모델
MOVIE
ID
TITLE
RUNNING_TIME
FEE_AMOUNT
FEE_CURRENCY
RESERVATION
ID
CUSTOMER_ID(FK)
SHOWING_ID(FK)
FEE_AMOUNT
FEE_CURRENCY
AUDIENCE_COUNT
RULE
ID
DISCOUNT_ID(FK)
POSITION
RULE_TYPE
DAY_OF_WEEK
START_TIME
END_TUME
SEQUENCE
DISCOUNT
MOVIE_ID(FK)
DISCOUNT_TYPE
FEE_AMOUNT
FEE_CURRENCY
PERCENT
SHOWING
ID
MOVIE_ID(FK)
SEQUENCE
SHOWING_TIME
CUSTOMER
ID
CUSTOMER_ID
NAME
27 / 문서의 제목
또 다른 데이터 표현 ­– Anemic
 Domain
 Model
Movie
id
title
runningTime
fee
Reservation
id
customerId
showingId
Amounr
audienceCount
Rule
id
discountId
position
ruleType
dayOfWeek
startTime
endTime
sequence
Discount
movieId
discountType
amount
percent
Showing
id
movieId
sequence
showingTime
Customer
Id
customerId
name
28 / 문서의 제목
데이터 접근 객체Data
 Access
 Object
MOVIE
ID
TITLE
RUNNING_TIME
FEE_AMOUNT
FEE_CURRENCY
RESERVATION
ID
CUSTOMER_ID(FK)
SHOWING_ID(FK)
FEE_AMOUNT
FEE_CURRENCY
AUDIENCE_COUNT
RULE
ID
DISCOUNT_ID(FK)
POSITION
RULE_TYPE
DAY_OF_WEEK
START_TIME
END_TUME
SEQUENCE
DISCOUNT
MOVIE_ID(FK)
DISCOUNT_TYPE
FEE_AMOUNT
FEE_CURRENCY
PERCENT
SHOWING
ID
MOVIE_ID(FK)
SEQUENCE
SHOWING_TIME
CUSTOMER
ID
CUSTOMER_ID
NAME
29 / 문서의 제목
어떻게 처리할 것인가 - 프로세스
30 / 문서의 제목
예매 처리 스크립트
MovieDAO
ReservationScript
reserveShowing(customerId,
 showingId,
 audienceCount)
DiscountDAO
RuleDAO
ShowingDAO
ReservationDAO
31 / 문서의 제목
절차적인 예매 로직
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
① 데이터베이스로부터 Movie, Showing, Rule 조회
② Showing에 적용할 수 있는 Rule 이 존재하는지 판단
③ if (Rule 이 존재하면) {
Discount를 읽어 요금 할인된 요금 계산
} else {
Movie의 정가를 이용해 요금 계산
}
④ Reservation 생성 후 데이터베이스 저장
}
32 / 문서의 제목
@Override
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = showingDAO.selectShowing(showingId);
Movie movie = movieDAO.selectMovie(showing.getMovieId());
ListRule rules = ruleDAO.selectRules(movie.getId());
② Showing에 적용할 수 있는 Rule 이 존재하는지 판단
③ if (Rule 이 존재하면) {
Discount를 읽어 요금 할인된 요금 계산
} else {
Movie의 정가를 이용해 요금 계산
}
④ Reservation 생성 후 데이터베이스 저장
}
① 데이터베이스로부터 Movie, Showing, Rule 조회
절차적인 예매 로직
33 / 문서의 제목
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = showingDAO.selectShowing(showingId);
Movie movie = movieDAO.selectMovie(showing.getMovieId());
ListRule rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
③ if (Rule 이 존재하면) {
Discount를 읽어 요금 할인된 요금 계산
} else {
Movie의 정가를 이용해 요금 계산
}
④ Reservation 생성 후 데이터베이스 저장
}
절차적인 예매 로직
② Showing에 적용할 수 있는 Rule 이 존재하는지 판단
private Rule findRule(Showing showing, ListRule rules) {
for(Rule rule : rules) {
if (rule.isTimeOfDayRule()) {
if (showing.isDayOfWeek(rule.getDayOfWeek()) 
showing.isDurationBetween(rule.getStartTime(), rule.getEndTime())) {
return rule;
}
} else {
if (rule.getSequence() == showing.getSequence()) {
return rule;
}
}
}
return null;
}
34 / 문서의 제목
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = showingDAO.selectShowing(showingId);
Movie movie = movieDAO.selectMovie(showing.getMovieId());
ListRule rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
Money fee = movie.getFee();
if (rule != null) {
fee = calculateFee(movie);
}
④ Reservation 생성 후 데이터베이스 저장
}
절차적인 예매 로직
③ if (Rule 이 존재하면) {
Discount를 읽어 요금 할인된 요금 계산
} else {
Movie의 정가를 이용해 요금 계산
}
private Money calculateFee(Movie movie) {
Discount discount = discountDAO.selectDiscount(movie.getId());
Money discountFee = Money.ZERO;
if (discount != null) {
if (discount.isAmountType()) {
discountFee = Money.wons(discount.getFee());
} else if (discount.isPercentType()) {
discountFee = movie.getFee().times(discount.getPercent());
}
}
return movie.getFee().minus(discountFee);
}
35 / 문서의 제목
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
Showing showing = showingDAO.selectShowing(showingId);
Movie movie = movieDAO.selectMovie(showing.getMovieId());
ListRule rules = ruleDAO.selectRules(movie.getId());
Rule rule = findRule(showing, rules);
Money fee = movie.getFee();
if (rule != null) {
fee = calculateFee(movie);
}
Reservation result = makeReservation(customerId, showingId, audienceCount, fee);
reservationDAO.insert(result);
return result;
}
절차적인 예매 로직
private Reservation makeReservation(int customerId, int showingId,
int audienceCount, Money payment) {
Reservation result = new Reservation();
result.setCustomerId(customerId);
result.setShowingId(showingId);
result.setAudienceCount(audienceCount);
result.setFee(payment);
return result;
}
④ Reservation 생성 후 데이터베이스 저장
36 / 문서의 제목
중앙 집중식Centralized 제어 스타일
reserveShowing()
showing = selectShowing()
rules = selectRules()
isTimeOfDayRule()*
discount = selectDiscount()
isAmountType()
getFee()
new
rules
:Rule
:Showing
DAO
showing
:Showing
:Reservation
Script
:Rule
DAO
:Discount
DAO
discount
:Discount
:Reservation
37 / 문서의 제목
아키텍처 패턴
Transaction Script
38 / 문서의 제목
Part
 - 4
도메인 모델Domain
 Model
39 / 문서의 제목
Process Data
Object
객체지향Object-Oriented
40 / 문서의 제목
많은 객체지향 서적의 주제
객체지향 기반의 도메인 레이어 설계
41 / 문서의 제목
객체지향 설계
협력하는 객체들의 공동체
객체지향 설계
주어진 책임을 수행하는 객체들의 협력
System
Showing
상영 정보를 알고 있다
예매 정보를 생성한다
Movie
CRC
 Card
Candidate(Role
 or
 Object)
Responsibility
Collaborator
책임과 협력을 표현하기 위한 객체지향 설계 도구
예매 생성 책임 할당
예매 생성에 필요한 정보의 전문가에게 할당Creator
Showing
상영 정보를 알고 있다
예매 정보를 생성한다
가격 계산 책임 할당
영화 가격 정보를 알고 있는 전문가에게 할당Information
 Expert
Movie
영화 정보를 알고 있다
가격을 계산한다
Showing
상영 정보를 알고 있다
예매 정보를 생성한다
Movie
Customer
할인율 계산 책임 할당
할인율을 적용할 책임을 가진 객체 추가
Movie
영화 정보를 알고 있다
가격을 계산한다
Showing
상영 정보를 알고 있다
예매 정보를 생성한다
Movie
Customer
Discount Strategy
Discount Strategy
할인 정책을 알고 있다
할인된 가격을 계산한다
할인 여부를 판단할 책임 할당
할인 정책을 판단하는 책임을 가진 Specification
 객체 추가
Movie
영화 정보를 알고 있다
가격을 계산한다
Showing
상영 정보를 알고 있다
예매 정보를 생성한다
Movie
Customer
Discount Strategy
할인 정책을 알고 있다
할인된 가격을 계산한다
Discount Strategy
Rule
할인 규칙을 알고 있다
할인 여부를 판단한다
Rule
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 fee.minus(discountStrategy.calculateDiscountFee(showing));
}
}
객체지향 구현
public abstract class DiscountStrategy {
public Money calculateDiscountFee(Showing showing) {
for(Rule each : rules) {
if (each.isStatisfiedBy(showing)) {
return getDiscountFee(showing);
}
}
return Money.ZERO;
}
abstract protected Money getDiscountFee(Showing showing);
public interface Rule {
boolean isStatisfiedBy(Showing showing);
}
public class SequenceRule implements Rule {
public boolean isStatisfiedBy(Showing showing) {
return showing.isSequence(sequence);
}
}
public class TimeOfDayRule implements Rule {
public boolean isStatisfiedBy(Showing showing) {
return showing.isPlayingOn(dayOfWeek) 
Interval.closed(startTime, endTime)
.includes(showing.getPlayngInterval());
}
}
객체지향 구현
객체지향 구현
public abstract class DiscountStrategy {
public Money calculateDiscountFee(Showing showing) {
for(Rule each : rules) {
if (each.isStatisfiedBy(showing)) {
return getDiscountFee(showing);
}
}
return Money.ZERO;
}
abstract protected Money getDiscountFee(Showing showing);
public class AmountDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee(Showing showing) {
return discountAmount;
}
}
public class NonDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee(Showing showing) {
return Money.ZERO;
}
}
public class PercentDiscountStrategy extends DiscountStrategy {
protected Money getDiscountFee(Showing showing) {
return showing.getFixedFee().times(percent);
}
}
Class
 Diagram
Showing
reserve(customer, count):Reservation
Rule
isStatisfiedBy(showing):boolean
AmountDiscount
Strategy
PercentDiscount
Strategy
NonDiscountStrategy SequenceRule TimeOfDayRule
DiscountStrategy
calculateFee(showing):Money
Movie
calculateFee(showing):Money
create
Customer
Reservation
Movie Discount Rule
1 0..1 1 1..*
1
0..*
1 0..*
Showing Reservation
Domain Concepts
Movie Discount Rule
1 0..1 1 1..*
1
0..*
1 0..*
Showing Reservation
Domain Concepts  Implementation
Showing
reserve(customer, count):Reservation
Rule
isStatisfiedBy(showing):boolean
AmountDiscount
Strategy
PercentDiscount
Strategy
NonDiscountStrategy SequenceRule TimeOfDayRule
DiscountStrategy
calculateFee(showing):Money
Movie
calculateFee(showing):Money
create
Customer
Reservation
54 / 문서의 제목
위임식delegated,
 분산식dispersed 제어 스타일
:Reservation :Movie
reserve()
new
:Showing
calculateFee()
calculateFee()
:DiscountStrategy :Rule
calculateDiscountFee()
isStatisfied()*
55 / 문서의 제목
아키텍처 패턴
Domain Model
56 / 문서의 제목
Part
 - 5
도메인 레이어와 아키텍처
57 / 문서의 제목
Presentation
Data
 Source
도메인 레이어가 전체 아키텍처 구성을 주도
Domain
58 / 문서의 제목
도메인 모델을 사용할 때
Showing
reserve(customer, count):Reservation
Rule
isStatisfiedBy(showing):boolean
DiscountStrategy
calculateFee(showing):Money
Movie
calculateFee(showing):Money
create
Customer
Reservation
AmountDiscount
Strategy
PercentDiscount
Strategy
NonDiscountStrategy SequenceRule TimeOfDayRule
59 / 문서의 제목
도메인 레이어 캡슐화

애플리케이션 아키텍처와 객체지향