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. 정리

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

책을 사서...

책장에 꽂아 둡니다.



아시는 분이 가지고 계신 이 책을 빌려서 잠시 읽은 적이 있다.

그 때는 무슨 내용일지 제대로 이해할 수 없었다.

지금도 그렇겠지... Orz...

그래도 지금은 요령이 생겨서... 한번의 완독을 하고, 시간이 지난 후 다시 완독하면서 '반복적인 책 읽기' 를 통해서 내가 쌓은 경험 만큼 깨달음을 얻을 수 있다는 요령을 터득했다.


프로그램을 짤 때는 자신과 컴퓨터뿐 아니라, 다른 사람들을 생각해야 한다!

이것이 구현 패턴이 전하는 메시지입니다. 이 책에서는 다른 사람을 배려하며 프로그래밍할 때의 경제적 이득을 강조했지만, 사실 이는 프로그래머 자신을 위한 것이기도 합니다. 자신이 더 큰 공동체의 일원임을 깨닫게 되면 공동체에 기여하는 것에 대한 만족을 느낄 수 있게 되기 때문입니다. 여러분도 이와 같은 만족감을 느낄 수 있으면 좋겠습니다. 즐거운 코딩 하시길.


- 미국 오레곤 주 메를린에서, 켄트 백 드림


기능적으로 올바르게 동작한다고 해서 모두 훌륭한 코드는 아니다. 훌륭한 코드는 프로그래머의 의도를 일관되게 전달해서, 다른 프로그래머들이 코드를 쉽게 이해하고 사용하며 자신 있게 수정할 수 있어야 한다. 그러나 훌륭한 코드는 쉽게 탄생하지 않는다. 훌륭한 코드는 프로그래머가 하루에도 수백 번 이상 내리는 작지만 중요한 결정의 산물이다. 이러한 중요한 결정들에 대해, 전설적인 소프트웨어 혁신자인 캔트 백이 "강력한 구현 패턴" 을 공개했다. 구현 패턴을 사용하면 더 간결하고 명쾌하며 체계적이고 비용 대비 효과적인 코드를 작성할 수 있다.


이 책은 다른 사람들이 이해하기 쉬운 코드를 만드는 프로그래밍에 대한 내용을 담고 있다. 하지만 너무 큰 기대는 금물이다. 아쉽게도 그런 코드를 만드는 비법 같은 것은 없다. 읽기 쉬운 코드를 작성하는 과정은 읽기 쉬운 글을 쓰는 것과 같다. 대상 독자를 정해야 하고, 명확한 전체 구조를 갖고 잇어야 하며, 전체 줄거를 생각해서 세부를 묘사해야 한다. 자바는 사람이 이해할 수 있는 코드를 작성하는 여러가지 방법을 제공한다. 이 책에는 읽기 쉬운 코드를 작성하는 자바 프로그래밍 습관을 모았다.


이 책은 "어떻게 하면 다른 사람들에게 코드를 전달(커뮤니케이션) 할 것인가?" 라는 고민에 대한 답이기도 하다. 프로그래머는 혼자 생각하면서 보내는 시간이 너무 많으므로, 다른 사람의 관점에서 코드를 바라보려 시도하는 것 자체가 커다란 변화이다. 프로그래머는 "컴퓨터가 이 코드를 어떻게 처리할까" 뿐 아니라 "내 생각을 다른 사람에게 어떻게 전달할까" 하는 고민까지 해야된다. 하지만 기존 코드를 이해하는 데 엄청난 소프트웨어 개발 비용이 투입되는 것을 감안하면, 이러한 변화는 건전할 뿐 아니라 경제적 이득을 가져올 수도 있다.


...


사실 이 책은 "좋은 코드는 중요하다" 라는 빈약한 전제를 기반으로 만들어졌다. 좋은 코드가 상업적 성공이나 광범위한 사용자 확보에 대한 필요조건 혹은 충분조건이라고 믿기에는, 사람들이 조잡한 코드로 돈을 많이 버는 사례를 너무 많이 봐왔다. 코드 품질이 회사나 개인의 미래를 좌우하는 요소가 아니라 할지라도, 나는 여전히 코드 품질이 매우 중요하다고 믿는다. 자신있게 코드를 개발, 출시하고 기회와 경쟁 상황에 따라 개발 방향을 바꿀 수 있으며 위기 속에서도 직원들의 사기를 높일 수 있는 회사는 조잡하고 버그가 있는 코드를 작성하는 회사에 의해 성공할 확률이 높다.


설사 좋은 코딩이 장기적으로 경제적 이득을 가져오지 못한다고 하더라도 나는 여전히 내가 작성할 수 있는 최고의 코드를 작성할 것이다. 인생이 70년이라 할 때 우리 인생은 20억 초에 불과하다. 그 소중한 순간들을 자랑스럽지 않은 일을 하면서 낭비하고 싶지는 않다. 코딩을 잘하는 것은 그 자체로도 프로그래머에게 만족감을 주지만, 다른 사람들이 내 코드를 이해하고 감탄해주며 내 코드를 사용하고 점차 발전시킨다는 점을 생각할 때 매우 중요하다.


결국 이 책은 책임감에 대한 이야기다. 여러분이 프로그래머로서 시간과 재능과 돈과 기회를 부여 받았다. 이러한 자원들을 책임감 있게 잘 사용하려면 어떻게 해야 하는가? 이 책은 이러한 고민에 대한 나의 답이다. 프로그래머는 자신과 CPU뿐 아니라, 자신의 코드를 보고 사용할 다른 사람들을 배려해서 코딩을 해야 한다.



도메인주도설계(Domain-Driven Design, DDD)  읽는 중



소프트웨어 시스템을 분리하는 방법.

계층화. 계층화의 핵심 원칙은 한 계층의 모든 요소는 오직 같은 계층에 존재하는 다른 요소나 계층상 "아래"에 위치한 요소에만 의존한다는 것이다. 위로 거슬러 올라가는의사소통은 반드시 간접적인 메커니즘을 거쳐야 하며...

계층화의 가치는 각 계층에서 컴퓨터 프로그램의 특정 측면만을 전문적므로 다룬다는 데 있다. 이러한 전문화를 토대로 각 측면에서는 더욱 응집력 있는 설계가 가능해지며, 이로써 설계를 더욱 쉽게 이해할 수 있다.


가장 바람직한 아키텍처 프레임워크라면 도메인 개발자가 모델을 표현하는 것에만 집중하게 해서 복잡한 기술적 난제를 해결한다.
...
프레임워크를 적용할 때 팀은 프레임워크의 목적에 집중해야 하는데, 그러한 프레임워크의 목적은 도메인 모델을 표현하고 해당 도메인 모델을 이용해 중요한 문제를 해결하는 구현을 만들어내는 데 있다.


한 프레임워크를 이용해 해결하기 힘든 갖가지 측면은 어려운 문제를 해결하고자...여러 프레임워크를 선택적으로 적용해서 극복할 수 있다. 프레임워크의 가장 유용한 기능만 분별력있게 적용한다면 구현과 프레임워크 간의 결합이 줄어들어 차후 설계 의사결정을 더욱 유연하게 내릴 수 있을 것이다.


도메인 로직이 프로그램상의 다른 관심사와 섞여 있다면 그와 같은 대응을 달성하기가 수월하지 않다. 따라서 도메인 주도 설계의 전제조건은 도메인 구현을 격리하는 것이다.

결국 도메인을 격리할 때의 가장 좋은 점은 부수적인 것을 배제하고 도메인 설계에만 집중할 수 있다는 것이다.


프로젝트에 도메인 모델은 있었지민 동작하는 소프트웨어를 개발하는 데 직접적으로 도움을 주지 않는 한 종이에 기록된 모델이 무슨 의미가 있겠는가?
...
도메인 주도 설계에서는 초기 분석 딘계에 도움이될 뿐 아니라 설계의 기반이 되는 모델이 필요하다.
...
코드와 그것의 기반이 되는 모델이 긴밀하게 연결되면 코드에 의미가 부여되고 모델과 코드가 서로 대응하게 된다.

DDD, chap 3. 모델과 구현의 연계


많은 개발자가 단지 프로그램 코드를 구성하는 데만 객체의 기술적 능력을 적용해 도움을 얻지만 객체 설계에서의 진정한 도약은 코드가 모델의 개념을 표현할 때 나온다.

모델에 기여하는모든 기술자는 프로젝트 내에서 수행하는 일차적인 역할과는 상관없이 코드를 접하는 데 어느 정도 시간을 투자해야만 한다. 코드를 변경하는 책임이 있는 모든 이들은 코드를 통해 표현하는 법을 반드시 배워야 한다. 모든 개발자는 모델에 관한 일정 수준의 토의에 깊이 관여해야 하고 도메인 전문가와도 접촉해야 한다.

국내의 많은 프로젝트들은 분석설계자와 개발자가 분리되어 있는 경우가 많다. 그 때문인지 분석설계된 모델이 개발자가 구현되는 단계에서 그 모습을 제대로 유지하지 못하고 구현 후에는 도메인 전문가들과 어울어지지 못하는 괴리감을 형성하며 불편해하는 경우가 잦다. 가능하다면, 프로젝트 초기부터 개발자들이 함께 참여하면서 분석설계를 하고 도메인을 정의하며 프로젝트를 시작해야 한다. 그러나 대부분의 프로젝트가 초기에 기획자와 아키텍트만 사전투입되고 개발자는 산발적으로 모집하여 진행하는 경우가 잦다. 이런 프로젝트는 대부분이... 실패할 가능성이 높다.


+ Recent posts