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

2018/10/15 사내세미나로 조영호님이 '애플리케이션 아키텍처와 객체지향’이라는 주제로 다음과 같은 주제로 발표하셨다.

1. 개요

Note
  • 애플리케이션 설계방식에 대한 설명 진행

    1. 절차지향 설계

    2. 객체지향 설계

2. 과정 설명

애플리케이션을 설계하는 과정에 대한 설명

  • 참가자들이 도메인에 대한 이해가 필요한 경우에는 천천히 진행

3. 도메인주도설계

  • 시스템을 만들기 위해 도메인 분석

Important

발표를 위해 잘 맞춰진 데이터모델과 도메인모델을 사용했다.

현실은 그렇게 호락호락하지 않아!

3.1. 도메인

  • 영화(Movie)

    • 제목

    • 상영시간

  • 상영(Showing): 영화가 재생되는 시간

    • 영화

    • 상영일시

    • 회차

    • 상영관

  • 할인정핵(DiscountStrategy)

    • 금액 할인(AmountDiscountStrategy): ex) 8,000 - 800 = 7,200

    • 비율 할인(PercentDiscountStrategy) ex) 8,000 - (8,000 * 0.1) = 7,200

  • 할인규칙(Rule)

    • 회차규칙(SequenceRule): 1회차(조조), 10회차

    • 시간규칙(TimeRule): 시간대

  • 예매(Reservation)

    • 상영

    • 인원

3.2. 도메인 예제

Movie(1) - (0..1)Discount(1) - (1..N)Rule

  • 도메인로직

    • 영화는 할인정책을 1개만 가질 수 있다.

    • 할인정책은 최소 1개 혹은 여러 개의 할인규칙을 가질 수 있다.

Note

이끼(8000원) 영화에 대해서 800원할인(금액할인)을 제공한다. 그 영화는 조조, 10회차, 월요일(10:00 ~ 12:00 상영), 목요일(18:00 ~ 21:00) 인 경우 할인한다.

4. 레이어 아키텍처

  • 표현/도메인/모델

    • 위에서 거른된 것들은 도메인 영역이다.

  • 절차지향적으로 작성하는 경우와 도메인적으로 작성하는 경우 모양이 다르게 된다.

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

5.1. 절차(Procedural)지향: 트랜잭션 스크립트(Transaction Script)

  • 데이터와 프로세스를 서로 다른 모듈에 구분짓는다.

  • 기능분할을 우선한다.

  • 프로세스를 분리하고 필요한 도메인을 도출한다.

  • 실무에서 사용하는 경우에는 운영데이터를 기준으로 한다.

    • 업무에 필요한 데이터 모델을 만드는 것을 우선한다. → 나쁜점: 이렇게 만들어진 데이터 모델이 설계를 주도한다.

    • 데이터 모델 1:1 대응 객체를 작성

      • 클래스를 데이터에 등록

      • 1 domain - 1 DAO

  • 예매처리 알고리즘

@Transactional
public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
  // 1. 데이터베이스로부터 Movie, Showing, Rule 조회
  // 2. Showing 에 적용할 수 있는 Rule 판단
  // 3. if(Rule 존재) {
  //    Discount를 읽어 할인금액 계산
  // } else {
  //    영화상영금액 계산
  // }
  //
  // 4. Reservation 생성 후 데이터베이스 저장
}

시퀀스 다이어그램을 그려보면 중앙집중식 제어(관련된 모델에 대한 제어가 한 곳에서 처리) 스타일이 나타나게된다.

5.2. 객체지향: 도메인 모델(Domain Model)

  • 절차지향과 차이점

    • 프로세스와 데이터를 하나의 객체(도메인) 안에 넣는다.

    • 객체는 행위와 데이터의 조합

5.2.1. CRC Card

  • 책임과 협력을 표현하기 위한 객체지향 설계 도구

  • Candidate(Role or Object): 후보

  • Responsibility: 후보가 수행할 역할

  • Collaborator: 후보와 협력할 객체

Important

기능에 대한 고민이 우선한다.

5.2.2. 예매 생성 책임할당

  • 어떤 기능을 개발해야하는지에 대해서는 정보 전문가에게 책임 할동

    • Reservation ← Movie

  • 영화상영 정보를 알고 있는 전문가에게 할당(Creator)

  • 영화가격 정보를 알고 있는 전문가에게 할당(Information Expert)

    • 영화가격정보를 가장 잘 알고 있는 것은 영화다.

  • 할인율을 적용할 책임을 가진 객체 추가

  • 할인정책을 판단하는 책임을 가진 Specification 객체 추가

  • 책임을 할당할 때는 결합도와 응집도를 따져야 한다. tradeOff 비용

5.2.3. 코드 구현

public class Showing {
    public Reservation reserve(Custom custom, int audienceCount) {}

    pubic Money calculate() {
      return movie.calculateFee(this).time(audienceCount);
    }
}

public Reservation {

}

public abstract class DiscountStrategy {
    public Money calculateDiscountFee(Showing showing) {

      return Money.ZERO;
    }
}

public interface Rule {

}

5.2.4. 위임식(delegated), 분산식(separated) 스타일

  • 도메인 모델

  • 다른 객체에게 위임하게 되면서 응집도는 높이고 결합도는 낮출 수 있다.

  • 아키텍처적인 관점에서 "도메인 모델"

6. 도메인 레이어와 아키텍처

  • 도메인 모델을 사용할 때

    • 객체를 기반으로 작성한 경우

      Note

      도메인 모델이 애플리케이션 아키텍처에 영향을 받아 뒤틀릴 수밖에 없다.

  • 트랜잭션과 DB 영향을 받음

6.1. 캡슐화

  • 트랜잭션을 잡기 위해 서비스 레이어를 구분짓는다.

6.1.1. 서비스 레이어

  • 애플리케이션 경계

  • 애플리케이션 경계 조합

  • 서비스 레이어

    @Transactional
    public ReservationService {
      public Reservation reserveShowing(int customerId, int showingId, int audienceCount) {
        Customer customer = customerRepository.findById(customerId);
        Showing showing = showingRepository.findById(showingId);
    
        Reservation reservation  = showing.reserve(customer, audienceCount);
    
        return reservationRepository.save(reservation);
      }
    }
  • 트랜잭션 스크립트를 사용할 떄

    • 비즈니스 로직이 서비스에 녹아있음

    • 별도의 서비스 레이어 불필요

  • 도메인 모델 단점

    • 도메인 모델을 사용할 때 DB매핑하는 것은 쉽지 않음

    • DB모델을 만드는 기준(중복제거)와 도메인모델을 만드는 기준이 서로 다름

6.1.2. 객체-관계 임피던스(impedance) 불일치 발생

  • 객체 모델과 DB스키마 사이의 불일치

  • 객체 패러다임과 관계 패러다임 간의 불일치

6.1.3. 데이터 매퍼

  • 객체모델과 DB 스키마 간의 독립성 유지

  • ORM(Object-relation Mapper): 객체와 엔티티 사이의 관계를 매핑

6.2. 트랜잭션 스크립트

  • DAO(테이블 데이터 게이트웨이)

7. 선택의 기로

우리가 짜는 프로그램은 두 가지 요구사항을 만족시켜야 한다. 우리는 오늘 완성해야 하는 기능을 구현하는 코드를 짜야하는 동시에 내일 쉽게 변경할 수 있는 코드를 짜야 한다.

— 샌디 메츠

어떤 변경이 발생할 지 알아야 잘 설계됐는지 여부를 알 수 있다.

Note

객체지향의 장점은 명사를 사용하여 개념을 유추할 수 있다는 것이다.

  • OCP(Open-Closed principal): 확장에는 열려있고 변경에는 닫혀있는 정책

  • 여러 영화할인규칙을 가지는 할인정보를 만든다.

  • 요구사항이 어떻게 변경될지 모른다면…​

  • 코드를 간단하게 작성하고 변경에 대한 대응하도록 작성하기

  • 변경이 발생할 때마다 리팩토링하여 변경하기 쉬운 코드

  • 행위에 집중되어 있기에 빈번한 변경이 발생할 수 있다.

    • 객체지향설계인 경우 변경에 대해서 코드를 변경하지 않고 확장가능한 코드를 작성할 수 있다.

도메인 모델복잡성을 알고리즘에서 분리하고 객체 간의 관계로 만들 수 있다.

유효성 검사, 계산, 파생 등이 포함된 복잡하고 끊임없이 변하는 비즈니스 규칙을 구현해야 한다면 객체모델을 사용해 규칙을 처리하는 것이 현명하다.

— Martin Fowler

8. 정리

명확하게 딱 떨어지는 도메인과 그에 부합하는 데이터모델을 설계하고 구현하는 일은 쉽지 않다. 비즈니스 로직을 끊임없이 분석하고 정의하여 그것을 구현하고 리팩토링해야 한다. 소프트웨어는 끊임없이 변화하는 그 순간까지는 살아있는 녀석이다.

+ Recent posts