나를 기록하다
article thumbnail
반응형

메인 슬라이드

[Part - 1] 아키텍처(Architecture)

  • 프로젝트에 참여하는 개발자들이 설계에 대해 공유하는 이해를 반영하는 주관적인 개념
  • 아키텍처는 주관적 → 같은 아키텍처라고 한다고 해도 서로 다른 팀에서 말하는 것이 세부적으로 다를 수 있음
  • 아키텍처를 변경하는 것은 매우 많은 비용이 든다. → 변경하기 어렵다.
    • 이유: 모든 개발자들이 현재 아키텍처를 공유하고 있기 때문
  • 아키텍처는 가급적 일찍 올바르게 결정해야 한다.
  • 아키텍처는 관심사의 분리이다.
    • 관심사의 분리는 서로 다르고 관련이 없는 책임들을 분리하는 것

레이어 아키텍처(Layered Architecture)

레이어드 아키텍처(Layered Architecture)

  • 서로 다른 관심사를 분리한 아키텍처
  • 유사한 것들을 한 레이어에 모아놓음으로써 그 레이어만 교체를 하면 전체 시스템을 다른 환경에서도 사용 가능
    → 유연성 & 재사용성 높아짐
  • 레이어는 팀마다 다를 수 있음 → 세분화 가능
  • 가장 중요한 레이어는 도메인(Domain)
    • 얼마나 도메인을 시스템에 잘 반영했느냐가 결정 
    • 시스템의 성공 원인: 사용자들이 많이 써주기  때문
    • 발을 딛고 있는 도메인을 최대한 가장 적절하게 반영한 시스템이어야지만 성공을 한다

도메인 레이어를 설계하는 방법

  • 도메인 레이어 설계하는 방법
    1. 절차 지향 : 트랜잭션 스크립트(Transaction Script)
      절차 지향적으로 개발하는 것이 대부분
    2. 객체 지향 : 도메인 모델(Domain Model)

[Part - 2] 영화 예매 도메인

도메인 개념

영화(Movie)
  • 제목, 상영시간
  • 영화는 메타 데이터라고 볼 수 있음
상영(Showing)
  • 영화, 상영일시 등
  • 고객이 실제로 예매하는 것은 영화가 아니라 상영
할인 정책(Discount)
  • Amount Discount: 특정 금액을 할인
  • Percent Discount: 현재 금액의 특정한 비율을 할인
할인 규칙(Rule)
  • Sequence Rule: 10회 상영인 경우 할인
  • Time Rule: 특정 요일 특정 시간인 경우 할인
예매(Reservation)
  • 영화, 상영 정보, 금액, 인원 등
  • 고객이 최종적으로 받을 정보

 

할인 정책 + 할인 규칙

할인 정책 + 할인 규칙

  • Discount는 하나만 사용 가능(없을 수도 있음)
  • Rule은 여러 개 존재 가능
  • Discount가 있으면 반드시 Rule이 존재

 

할인 적용

할인 적용

 

예매

예매

  • 결과적으로 예매라는 도메인이 반환

 

도메인 개념 또는 후보 객체

  • 영화는 여러 개(0개 이상)의 상영을 가진다.
  • 상영은 여러 개(0개 이상)의 예매를 가진다.
  • 영화는 0 또는 1개의 할인 정책을 가진다.
  • 할인 정책은 1개 이상의 규칙을 가진다.
도메인이란 개발할 대상이다.
시스템 상에서 가져야하는 부분, 즉 시스템에 넣는 부분을 도메인이라고 한다.

[Part - 3] 트랜잭션 스크립트(Transaction Script) - 절차지향

절차적

  • 트랜잭션 스크립트는 절차적으로 짠다
    • 내가 다뤄야하는 데이터와 이를 조작하는 프로세스가 따로 동작한다는 의미
    • 따라서 데이터와 프로세스를 따로 고민한다

 

데이터 모델

데이터 모델

에너믹 도메인 모델(Anemic Domain Model)

Anemic Domain Model

  • 데이터 모델과 똑같은 클래스를 배치, getter와 setter 넣음
  • 로직이 없음
  • 테이블이 있고 테이블의 필드를 그대로 클래스의 속성으로 매핑한 클래스들을 여러개 만들어 둔 것을 에너믹 도메인 모델이라 함
데이터 접근 객체(DAO)

데이터 접근 객체

  • 클래스에 해당하는 DAO를 배치(각 테이블에 필요한 DAO를 구현)
예매 처리 스크립트

예매 처리 스크립트

  • ReservationScript(일반적으로 ReservationService)를 만듦
  • 데이터를 읽을 DAO들을 injection
절차적인 예매 로직
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
    1) 데이터 베이스로부터 Movie, Showing, Rule 조회
    2) Showing에 적용할 수 있는 Rule이 존재하는지 확인
    3) if(Rule이 존재하면) {
            Discount를 읽어 할인된 요금 계산
       } else {
            Movie의 정가를 이용해 요금 계산
       }
    4) Reservation을 생성 후 데이터베이스에 저장
}
  • 대부분 위와 같이 절차적으로 구현
  • 도메인이 복잡해지면 객체지향이 적합

1. 데이터베이스로부터 Movie, Showing, Rule 조회

@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
    // 1) 데이터 베이스로부터 Movie, Showing, Rule 조회
    // 필요한 데이터를 모두 준비한다.
    Showing showing = ShowingDAO.selectShowing(showingId);
    Movie movie = MovieDAO.selectMovie(showing.getMovieId());
    List<Rule> rules = ruleDAO.selectRules(movie.getId());

    // ...
}

2. Showing에 적용할 수 있는 Rule이 존재하는지 확인

@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
    Showing showing = ShowingDAO.selectShowing(showingId);
    Movie movie = MovieDAO.selectMovie(showing.getMovieId());
    List<Rule> rules = ruleDAO.selectRules(movie.getId());

    // 2) Showing에 적용할 수 있는 Rule이 존재하는지 확인
    Rule rule = findRule(showing, rules);

    // ...
}

private Rule findRule(Showing showing, List<Rule> 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;
      }
    }
  }
}

3. Rule의 존재 여부

@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
    Showing showing = ShowingDAO.selectShowing(showingId);
    Movie movie = MovieDAO.selectMovie(showing.getMovieId());
    List<Rule> rules = ruleDAO.selectRules(movie.getId());

    Rule rule = findRule(showing, rules);

    /*
    3) if(Rule이 존재하면) {
            Discount를 읽어 할인된 요금 계산
       } else {
            Movie의 정가를 이용해 요금 계산
       }
    */
    Money fee = movie.getFee();
    if (rule != null) {   // Rule이 null이 아니면 할인
        fee = calculateFee(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);
}

4. Reservation을 생성하여 데이터베이스에 저장

@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
    Showing showing = ShowingDAO.selectShowing(showingId);
    Movie movie = MovieDAO.selectMovie(showing.getMovieId());
    List<Rule> rules = ruleDAO.selectRules(movie.getId());

    Rule rule = findRule(showing, rules);

    Money fee = movie.getFee();
    if (rule != null) {   // Rule이 null이 아니면 할인
        fee = calculateFee(movie);
    }

    // 4) Reservation을 생성해 데이터베이스에 저장
    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;
}
데이터를 가져오고, 로직을 실행하는 것이 서비스 하나에서 모두 수행된다.
수많은 if, else, then 로직을 포함한다.

 

중앙 집중식(Centralized) 제어 스타일

중앙 집중식(Centralized) 제어 스타일

  • 서비스에서 모든 Control이 다 일어나고 모든 것을 판단한다.
  • 시퀀스 다이어그램을 그려봤을 때, 어떤 제어가 한쪽으로 다 몰린다고 하면 트랜잭션 스크립트라고 보면 된다.
    → 절차지향적 시퀀스 다이어그램

[Part - 4] 도메인 모델(Domain Model)

도메인 모델

  • 프로세스와 데이터를 하나의 덩어리로 묶어서 생각한다.
  • 객체지향 기반의 도메인 레이어 설계
  • 객체지향은 협력하는 객체들의 공동체이다.
    • 객체들이 메세지를 주고 받으면서 어플리케이션을 만들기 위해 협력하는 과정을 만들어간다.
    • 객체지향적으로 설계한다는 것은 역할을 나누고 책임을 부여한다. 그리고 여러 객체들이 협력하여 상호작용한다.

 

객체지향 설계

객체지향 설계

  • 객체 하나만으로는 아무것도 하지 못한다.
  • 객체 사이의 협력을 하는 매게체는 메세지를 전송하는 것이다.

 

CRC Card

CRC Card

  • 객체지향 설계를 할 때 가장 많이 사용하는 객체지향 설계 도구
  • 실제로 만질 수 있는 물리적인 물체를 사용하여 객체들의 상호작용을 설계(실제로는 종이, 인덱스 카드)
  • 객체를 나타내는 종이들을 실제로 적고, 옮기고, 배치하면서 적절한 객체간의 구조를 찾아가는 과정
  • 구간별 설명: 후보, 책임, 협력자
    • 후보(Candidate): 역할 또는 객체
    • 책임(Responsibility): 객체의 책임
    • 협력자(Collaborator): 이 객체가 이 책임을 할 때 누구에게 메세지를 보내야 하거나, 누가 필요한 지
  • 객체지향은 데이터에 대한 이야기를 하지 않는다. 대신 어떤 역할이 필요한지 어떤 객체가 필요한지 이야기한다.
  • 객체는 상태와 행동을 같이 가진다.
    • 상태와 행동을 같이 가진다는 것은 어떤 상태가 있을 때 그 상태를 변경하고 조작하는 행동이 같이 있어야 한다.
    • 그 상태를 가장 밀접하게 사용하는 행동을 그 객체에 배치해야 한다.
    • 행동과 상태를 같이 넣어라. 아니면 캡슐화가 깨진다.

 

예매 생성 책임 할당

예매 생성 책임 할당

  • 이 책임을 수행하는 데 필요한 데이터를  가장 많이 가지고 있는 객체에 행동을 할당
  • 객체를 만드는 순서는 어플리케이션의 요구사항을 분석하고 그 다음에 적절한 객체를 선정
  • 예매 생성은 상영에게 책임을 할당(가장 많은 데이터를 가지고 있기 때문)
    • 이게 되지 않으면 별도의 팩토리와 같은 객체를 만들어야 함
가격 계산 책임 할당

가격 계산 책임 할당

  • 영화 가격을 알고 있는 영화 객체에 할당
할인율 계산 책임 할당

할인율 계산 책임 할당

  • Discount Strategy 객체를 생성하여 할인율 계산 책임을 할당
  • Movie는 Discount Strategy와 협력
할인 여부를 판단할 책임 할당

할인 여부를 판단할 책임 할당

  • Rule에 할인 여부를 판단할 책임 할당
  • Discount Strategy는 Rule과 협력
- 개념적으로 어떤 객체들이 어떤 책임을 가져야 하는지는 어플리케이션 요구사항부터 의도
- 어플리케이션 요구사항을 바탕으로 객체들을 생성하고 역할을 부여하며 책임을 할당하고 협력하도록 함
- 이런식으로 전체 객체간의 협력관계를 통해 어플리케이션을 구현하는 것이 객체지향적으로 설계하는 것

객체지향 구현

상영(Showing), 예약(Reservation), 영화(Movie)
public class Showing {
    public Reservation reserve(Customer customer, int audienceCount) {
        return new Reservation(customer, this, audienceCount);
    }

    public Money calculateFee() {
        return movie.calculateFee(this);
    }
}

public class Reservation {
    Reservation(Customer customer, Showing showing, int audienceCount) {
        this.customer = customer;
        this.showing = showing;
        this.fee = showing.calculateFee().times(audienceCount);
        this.audienceCount = audienceCount;
    }
}

public class Movie {
    public Money calculateFee(Showing showing) {
        return fee.minus(discountStrategy.calculateDiscountFee(showing));
    }
}
할인 전략(DsicountStrategy)
public abstract class DiscountStrategy {
    public Money calculateDiscountFee (Showing showing) {
        for(Rule each : rules) {
            if(each.isSatisfiedBy(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);
    }
}
할인 규칙(Rule)
public interface Rule {
    boolean isSatisfiedBy(Showing showing);
}

public class SequenceRule implements Rule {
    public boolean isSatisfiedBy(Showing showing) {
        return showing.isSequence(sequence);
    }
}

public class TimeOfDayRule implements Rule {
    public boolean isSatisfiedBy(Showing showing) {
        return showing.isPlayingOn(dayOfWeek) &&
               Interval.closed(startTime, endTime)
                       .includes(showing.getPlayingInterval());
    }
}
  1. Showing에 reserve 메서드
    • Reservation(customer, this, audienceCount) 반환
  2. Reservation의 fee → Showing에게 금액 계산 할당
    • showing.calculationFee().times(audienceCount)
  3. Showing에서 금액을 계산하는 것은 Movie에게 위임(가격을 가장 많이 알고 있는 객체가 Movie)
    • movie.calculateFee(this)
  4. Movie는 다시 DiscountStrategy에게 얼마를 할인해야 되는지 물어봄
    • discountStrategy.calculateDiscountFee(showing)
  5. DiscountStrategy는 n개의 Rule 안에서 루프를 돌며 하나라도 만족을 하는지 판단
    • isSatisfiedBy(showing)
  6. 맞으면 금액을 계산하기 위해 getDiscountFee라고 하는 protected 메서드 호출
    • getDiscountFee(showing)
  7. 각각의 서브 클래스들이 그 조건에 맞게 계산
Domain Concepts & Implementation

Domain Concepts & Implementation

  • 객체지향으로 설계한다는 것은?
    • 객체들의 구조는 도메인 안에 들어 있는 객체들의 관계와 최대한 유사하게 클래스의 구조를 가져가는 것.
    • 적절한 객체에 책임을 할당하고 객체 간의 협력 관계로 푸는 것이 객체 지향이다.
위임식(delegated), 분산식(dispersed) 제어 스타일

위임식(delegated), 분산식(dispersed) 제어 스타일

  • 트랜잭션 스크립트와는 달리 책임이 분배되어 있는 시퀀스 다이어그램

[Part - 5] 도메인 레이어와 아키텍처

도메인 레이어, 도메인 모델

  • 도메인 레이어를 어떻게 짜느냐에 따라 전체 아키텍처의 구성이 달라진다.
  • 도메인 모델을 사용하면 이 안에 들어있는 것은 순수한 도메인 로직
    1. 도메인 로직만을 가지고 어플리케이션을 만들 수 없다.
    2. 이 객체를 만들기 위해 필요한 데이터를 데이터베이스에서 가져와야 한다.
    3. 할인 정보는 실제로 데이터베이스에 있다.
    4. 데이터베이스의 정보를 객체에 밀어 넣는 작업이 필요하다.
  • 도메인 로직을 처리할 때 필요한 전후 작업이 있다.
    • 순수한 도메인 로직이 아닌 로직을 처리하기 위한 전후 관계, 어플리케이션 flow를 관리하는 것을 어플리케이션 로직이라 한다.
      [예시] DB에서 읽어오는 작업, DB 넣는 작업, 메일이나 카톡으로 push하는 등과 같은 후 작업)
  • 어플리케이션 로직은 일반적으로 순수한 객체(도메인 객체) 안에 넣지 않는다.
    • 의존성(dependency)이 생김(DB나 Network)
    • 테스트 어려움, 응집도 떨어짐
    • 도메인 레이어를 캡슐화 해야한다
  • 기본적으로 도메인과 무관한 로직이 도메인에 침투하지 못하도록 막아야 한다.
  • 이러한 어플리케이션 로직을 모아둔 곳이 서비스 레이어다.

서비스 레이어

  • 도메인 모델 패턴으로 도메인 모델을 구성하면 그 위쪽에 서비스 레이어가 생성된다.
  • 서비스 레이어의 역할
    • 어플리케이션 경계
    • 어플리케이션 로직 처리(application logic)
    • 도메인 로직의 재사용성 촉진

서비스 레이어

  • 위와 같이 분리하면 하나의 도메인 레이어를 다양한 서비스 레이어에서 재사용이 가능하다.
트랜잭션 경계

트랜잭션 경계

  • 트랜잭션도 어플리케이션 로직이라고 볼 수 있다.
  • 서비스 레이어를 만들면서 트랜잭션 경계가 생긴다.
  • 서비스 레이어에서 트랜잭션 경계를 설정한다.
// 도메인 모델에서 서비스 레이어 로직
@Transactional
public Reservation reserveShowing(int reserverId, int showingId, int audienceCount) {
  Customer reserver = customerRepository.find(reserverId);
  Showing showing = showingRepository.find(showingId);

  Reservation reservation = showing.reserve(reserver, audienceCount);

  reservationRepository.save(reservation);

  return reservation;
}
  • 서비스 레이어는 도메인 레이어를 처리하기 위한 준비 작업(전처리), 도메인 레이어가 잘 처리되었거나 예외가 발생했을 때의 예외 처리(후처리)가 있다.
  • 관심사의 분리(어플리케이션의 로직은 서비스, 도메인 로직은 도메인 레이어)
트랜잭션 스크립트를 사용할 때
@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
    Showing showing = ShowingDAO.selectShowing(showingId);
    Movie movie = MovieDAO.selectMovie(showing.getMovieId());
    List<Rule> 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;
}
  • 트랜잭션 스크립트는 트랜잭션 관리, 어플리케이션 로직, 도메인 로직이 한 곳에 있다.
    → 어떤 코드를 어디서 변경해야 하는지를 찾아가기가 힘들다(숨어 있음)
  • 별도의 서비스 레이어가 필요하지 않다.
  • 본질적으로 위 코드는 도메인 레이어가 맞다.
데이터 소스 레이어(RDB)

데이터 소스 레이어는 일반적으로 관계형 데이터베이스

  • 데이터 소스는 대부분 관계형 데이터베이스(RDB)를 사용한다.
  • 도메인 모델을 사용할 때 테이블 설계는 어렵다.
객체-관계 임피던스 불일치

객체-관계 임피던스 불일치

  • 객체 모델과 DB 스키마 사이의 불일치를 말한다.
  • 객체 패러다임과 관계 패러다임의 불일치
    • 객체 패러다임: 객체들의 행위 중심, 다형성, 객체의 유연성, 재사용성(수정 용이성) 중점
    • 관계 패러다임: 데이터 관점은 중복 제거 중점
  • 객체 지향적으로 구현할수록 관계 패러다임과 멀어진다.

 

데이터 매퍼(Data Mapper)

데이터 매퍼(Data Mapper)

  • Data Mapper를 둬서 양쪽의 불일치를 해결해준다.
    • 객체와 DB 관계를 끊는다.
    • Data Mapper는 직접 구현하기 어렵다. 따라서 하이버네이트와 같은 ORM을 사용한다.
  • 대표적으로 JPA가 존재한다.(Java Persistence API)
    • 객체 지향적으로 개발하기 위해 DB와 객체지향이 서로의 길을 가기 위해 중간에서 연결해주는 것이 JPA이다.
    • JPA를 사용한다고 해도 여러가지 제약조건이 존재하기에 순수하게 객체 지향적으로 구현하기는 불가능하다.

 

테이블 데이터 게이트웨이(Table Data Gateway)

테이블 데이터 게이트웨이 1
테이블 데이터 게이트웨이 2

  • 데이터 관점 테이블과 1:1로 매핑되는 데이터 덩어리를 만들고 getter & setter를 넣는다.
  • 데이터마다 DAO를 생성한다.
  • DAO라는 객체가 하나의 테이블을 처리하게 만든다.(Join은 예외)
  • 하나의 테이블마다 하나의 DAO라는 객체를 넣고,. 그 객체가 그 테이블을 감당하게 한다.
  • 이것을 테이블 데이터 게이트웨이라고 한다.

 

Domain Model / Transaction Script

Domain Model / Transaction Script

도메인 모델(Domain Dodel)
  • 도메인 모델로 가면 서비스 레이어가 일반적으로 들어가야 한다.
  • 들어가지 않을 시 도메인 모델의 독립성이 저하된다.
  • Data Source는 일반적으로 ORM과 같은 Data Mapper를 써서 구성
트랜잭션 스크립트(Transaction Script)
  • 일반적으로 Transaction Script를 구성할 때는 서비스 레이어가 없음.
  • Data Source는 Table Data Gateway를 사용(각 테이블마다 개별 DAO를 생성)
  • 전체 레이어를 관통하는 getter & setter를 가진 Anemic Domain Model을 사이에 둔다.

[Part - 6] 선택의 기로에서

무엇을 쓸 것인지 결정해야 한다.

변하지 않는 것은 모든 것이 변한다는 사실 뿐이다.
헤라클레이토스

할인을 중복으로 해야하는 요구사항이 들어왔을 경우

기존 구조의 변경
Discount가 여러 개 적용이 가능하도록 변경

  • 요구사항으로 금액과 퍼센트 할인 둘다 들어옴
  • 여러 개의 할인을 적용할 수 있도록 로직을 수정
트랜잭션 스크립트
private Money calculateFee(Movie movie) {
  List<Discount> discounts = DiscountDAO.selectDiscounts(movie.getId());

  Money discountFee = Money.ZERO;
  for (Discount discount : discounts) {
    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);
}
  • 기존의 코드를 수정한다.
    • Discount가 n개가 가능
    • 1개가 아니라 n개를 select하도록 가져와서 loop를 돌려 type을 체크 후 금액을 누적시킨다.
  • 기존 코드를 수정한다는 두려움
    • 테스트 코드가 없다면 더욱 심화된다. 실무에서도 테스트 코드 없이 리팩토링하는 경우 존재
  • 개념이 암묵적으로 숨겨진다.
    • 중복 할인 로직을 코드에서 찾을 수 없다.
    • 전체 로직을 모두 읽어봐야 한다.
    • What이 아닌 How를 판단해야 한다.

 

컴포지트 디자인 패턴

컴포지트 디자인 패턴

public class OverlappedDiscountStrategy extends DiscountStrategy {
  // ...
}
  • 도메인 모델은 기존의 코드를 수정하지 않고, 컴포지트 디자인 패턴으로 해결할 수 있다.
    • 기존 코드의 수정에 닫혀있는 상태에서, 새로운 코드의 추가에는 열려 있다 → OCP
  • 클래스로 나눌 때의 장점: 암묵적이었던 개념을 명시적으로 표현할 수 있다.
    • OverlappedDiscountStrategy라는 이름의 클래스를 추가했으므로, 이름만 보고 알 수 있다.

 

의존성 역전 원칙(DIP)

의존성의 방향

  • 상위 레벨이 하위 레벨의 의존성을 가지지 않는다.
  • 의존성 방향이 추상화 방향(High Level)으로 간다.
  • 이것을 의존성 역전 원칙(DIP)라고 한다.

 

협력 관계와 추상화

협력 관계와 추상화

  • 추상화를 사용하면 도메인의 개념을 확 줄여서 이야기할 수 있다. 필요 시 detail로 내려갈 수 있다.
  • 왼쪽 그림과 같은 협력관계로 코드를 짜면 변경이 어려움
  • Domain Abstraction을 뽑고 뽑고 뽑으면 Domain Layer Architecture가 된다.

 

도메인 모델은 복잡성을 알고리즘에서 분리하고 객체 간의 관계로 만들 수 있다.
유효성 검사, 계산, 파생 등이 포함된 복잡하고 끊임없이 변하는 비즈니스 규칙을 구현해야 한다면 객체 모델을 사용해 비즈니스 규칙을 처리하는 것이 현명하다.
Martin Fowler

 

거짓말

  1. 영화 예매 시스템을 통해 도메인 모델의 장점을 쉽게 볼 수 있었다.
  2. 대부분의 현재 코드는 요구 사항 변경에 부적합
  3. 대부분의 어플리케이션의 요구사항은 어떻게 변화될지 아무도 모른다.
  4. 요구사항이 어떻게 변경될지 모르므로 코드를 최대한 간단하게 구현해야 한다.
  5. 변경이 필요할 때마다 리팩토링이 필요하다.
  6. 하지만 변경의 방향을 알 수 있다면 객체지향 패러다임이 변화를 수용할 수 있는 설계를 지원한다. 단, 변경의 방향을 알기 위해서는 경험을 쌓아야 한다.
반응형

'Review > Seminar' 카테고리의 다른 글

[우아한테크세미나]TDD와 리팩토링  (0) 2023.09.28
profile

나를 기록하다

@prao

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!

profile on loading

Loading...