본 포스트는 도메인 주도 설계 라는 책을 참조하였습니다. 가능하면 본 책을 직접 보시고 DDD를 이해하는 것을 추천드립니다. http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=12174216

  • 도메인 모델은 프로젝트를 위한 공통언어의 핵심
  • 모델 기반 의사소통은 UML 상의 다이어그램으로 한정되어서는 안된다
  • 다이어그램이 표현하고자하는 의미가 포함되어야한다.

공통 언어?

UBIQUITOUS LANGUAGE (보편 언어)

도메인 전문가 VS 개발자 사이의 언어 차이 과연 어떻게 해결할 수 있는가? 의 고민에서 부터 출발

  • 언어의 분열로 인한 도메인 전문가와 개발자 사이의 업무 이해도 저하 - 각각 따로 도메인 이해
  • 코드, 개발자, 도메인 전문가 사이의 단절 및 오해
  • 잦은 번역으로 인한 의사소통이 무뎌지고, 지식탐구가 빈약해짐
  • 모든 사람 이 이해할 수 있어야지 공통 언어 라고 할 수 있다.

도메인 모델은 공통언어의 근간을 제공하고 동시에 팀 내 의사 소통을 소프트웨어 구현까지 연결할 수 있다.

지식탐구 과정 중 언어가 변화 되면 필연적으로 도메인 모델 변화 로 이어지며, 코드상으로는 리펙토링 이 된다.

즉,

모델을 언어의 근간으로 사용하라. 팀 내 모든 의사소통과 코드에서 해당 언어를 끊임없이 적용하는 데 전념하라. 다이어그램과 문서에서, 그리고 특히 말할 때 동일한 언어로 사용하라

  • 도메인 전문가는 도메인을 이해하는 데 부자연스럽고 부정확한 용어나 구조에 대해 반대 의사를 표명해야한다.
  • 개발자는 설계를 어렵게 만드는 모호함과 불일치를 찾아내는 데 촉각을 곤두세워야 한다.

큰 소리내어 모델링하기

말하기(의사소통), 쓰기(문서화, 모델링) 간 언어 일치

소리내어 말하는 모델링 고도화

  1. “Routing Servie에 출발지, 목적지, 도착 시각을 전달하면 화물이 멈춰야 할 지점을 찾고, 음… 그것을 데이터베이스에 삽입한다.”
    • 모호하고 기술적임
  2. “출발지, 목적지, 등등… 이것들을 모두 Routing Service에 넣으면 필요한 것이 모두 담긴 Ltinerary(운항일정)를 돌려받는다.”
    • 좀 더 완전해졌지만, 장황함
  3. “Routing Service는 Route Specification(항로설정명세)을 만족하는 Ltinerary를 찾는다”
    • 간결함

시스템에 관해 이야기를 주고받을 때 모델을 사용하라. 모델의 요소와 상호작용을 이용하고 모델이 허용하는 범위에 개념을 조합하면서 시나리오를 큰 소리로 말해보라. 표현해야 할 것을 더 쉽게 말하는 방법을 찾아낸 다음 그러한 새로운 아이디어를 다이어그램과 코드에 적용하라

한 팀, 한 언어

도메인 전문가와 개발자는 도메인 모델을 통해서 소통해야한다.

수준 높은 도메이 전문가도 해당 모델을 이해하지 못한다면 모델이 뭔가 잘못된 것이다.

도메인 전문가와 개발자 사이에 언어적 분열이 일어나서는 안된다.

  • 보편 언어 : 개발자와 도메인 전문가 간 교집합
    • 도메인 모델 용어
    • BOUNDED CONTEXT(제한된 컨텍스트)의 이름
    • 대규모 구조의 용어
    • 이 책의 여러 패턴 이름
    • 개발자가 이해하지 못하는 보편 언어의 확장 영역 : 개발자가 이해하지 못하는 업무 관련 용어, 모든 이들이 사용하지만 설계에 나타나지 않는 용어
  • 개발자 언어
    • 설계의 기술적인 측면
    • 기술적인 용어
    • 기술적인 디자인 패턴

문서와 다이어그램

  • 소프트웨어 설계 시 꼭 시각화된 UML 다이어그램 (또는 비슷한 것)을 그리자.
  • 형식은 중요한 것이 아니다. 다이어그램 자체가 참석자 간 구심점 역활을 하면 된다.
  • UML 또한 언어 이다.
  • 모델링의 기본 개념은 나무를 보는 것이 아니라 숲을 보는 것 이다. 즉 모든 것을 포괄할 필요도 너무 구체화 할 필요도 없다.

UML의 표현의 한계 2가지

  1. 모델이 나타내는 개념의 의미
  2. 모델 내 객체의 행위
    • 객체의 상호작용(또는 Sequence) 다이어그램으로 암묵적으로 표현 가능

하지만 인간의 언어(자연어)를 통해서 한계를 극복할 수 있다.

  • 다이어그램 은 의사소통과 설명의 수단이며 브레인스토밍을 촉진한다. 이러한 목적은 다이어그램이 최소화됐을 때 가장 잘 전달된다.
  • 다이어그램들은 설계상의 제약조건을 보여주지만 모든 세부사항에 걸친 설계 명세는 아니다. 그것들은 아이디어의 골자 를 나타낼 뿐이다.
  • 설계의 생생한 세부사항은 코드에 담긴다.
  • 모델은 다이어그램이 아니라는 점을 항상 명심해야 한다. > 다이어그램이 표현하고자 하는 의미이다.

글로 쓴 설계 문서

구두(자연어)에 의한 의사 소통은 코드의 정연함과 상세함을 의미적으로 보충한다. 하지만 어느 정도의 문서(글)이 없다면 기록되지 않으므로 구두적인 내용을 다시 상기시킬 수 없게 된다.

기록은 기억을 지배한다. 고로, 어느 정도는 글로 쓴 문서로 안정과 공유를 꾀할 필요가 있다.

문서를 평가하는 두 가지 일반적인 지침

  1. 문서는 코드와 말을 보완하는 역할을 해야 한다.
    • 문서는 코드가 이미 잘 하고 있는 것을 하려고 해서는 안 된다.
    • 세부사항과 프로그램 행위의 정확한 명세는 코드에 양보한다.
  2. 문서는 유효한 상태를 유지하고 최신 내용을 담고 있어야 한다.
    • 설계 문서의 가장 큰 가치는 모델의 개념을 설명하고, 코드의 세부사항을 파악해 나가는데 도움을 주며, 어쩌면 모델의 의도된 사용 방식에 어떤 통찰력을 주는 데 있다.
    • 문서는 프로젝트 활동과 관련을 맺고 있어야한다. : UBIQUITOUS LANGUAGE가 문서(다이어그램), 코드, 대화에 나타나야한다.
    • 문서의 이력관리가 되지 않는다면 유효한 상태로 유지되면서 혼란을 일으키고 프로젝트에 악영향을 줄 가능성이 크다.
    • 문서를 최소한으로 유지하고 코드와 대화를 보완하는데 집중함 > 프로젝트와 연관된 상태로 유지가능
    • UBIQUITOUS LANGUAGE와 그것이 발전이 문서를 유효한 상태로 유지하고 프로젝트 활동과 결부되게 만드는 구심점으로 삼아라

프로젝트 활동 : 코드, 문서(다이어그램), 대화

실행 가능한 기반 ??

코드에 대한 테스트

올바르게 실행 되는 것 뿐만 아니라 올바른 의미를 전달 하는 코드를 작성하자면 엄청나게 세심한 노력을 기울어야 한다.

현재의 표준 기술(ex:JUnit)을 활용해 코드의 행위, 의도, 메시지를 일치시키려면 훈련과 설계에 관한 어떤 특정한 사고방식이 필요하다.

설명을 위한 모델 ??

하나의 모델 을 통해서 구현, 설계, 의사소통(프로젝트 활동)의 기초가 돼야 한다. 하지만 설명을 위한 모델은 ??

  • 기술적 모델(from 개발자) : 최소한의 수준으로 엄격하게 - 객체 (or 클래스) 다이어그램
  • 설명을 위한 모델(from 도메인 전문가) : 다양한 방식으로 표현할 수 있다.
    • 도메인 전문가가 개발자들를 교육하고자 하는 경우 다양한 방식으로 사용
    • 오히려 UML적일 필요가 없으며, 오히려 그렇지 않을 때가 일반적으로 가장 좋다.
  • 설명을 위한 모델과 설계를 주도하는 모델(기술적 모델) 간의 차이를 의식하고 있어야한다. 즉, 두 모델은 같을 필요가 없다.

기술적 모델과 설명을 위한 모델이 함께할 때 이해하기가 쉽다

예제 : 해운 활동과 항로

그림 2-4

기술적 모델

그림 2-5

설명을 위한 모델

본 포스트는 도메인 주도 설계 라는 책을 참조하였습니다. 가능하면 본 책을 직접 보시고 DDD를 이해하는 것을 추천드립니다. http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=12174216

효과적인 모델링의 요소

  1. 모델과 구현의 연계.
  2. 모델을 기반으로 하는 언어 정제.
  3. 풍부한 지식이 담긴 모델 개발.
  4. 모델의 정제.
  5. 브레인스토밍과 싪험.

위와 같은 지식탐구는 팀 내 지식을 가치있는 모델로 만든다

지식 탐구

  • 지식 탐구 팀 = 개발자(주도) + 도메인전문가
  • 원재료
  • 도메인 전문가의 지식
  • 기존 시스템 사용자
  • 동일 도메인에 관련된 레거시 시스템의 기술팀
  • 이전의 비슷한 프로젝트에서 얻은 경험

개념적 엄밀함(conceptual rigor)

지속적인 학습

소프트웨어를 작성하기 시작할 때 우리는 충분히 알지 못한 상태에서 시작한다.

  • 생산성이 뛰어난 팀은 지속적인 학습 을 바탕으로 의식적으로 지식을 함양한다.
  • 팀 구성원이나 개발자, 도메인 전문가에게서 모두 똑같이 지식을 얻고 의사소통 체계를 공유하며, 구현을 거쳐 피드백 고리를 완성하는 일을 모두 효과적으로 수행하는 지식탐구 프로세스를 궤도에 올리는 것
  • 발견을 위한 항해는 어디선가 시작돼야 한다.

풍부한 지식이 담긴 설계

  • 엔티티와 값 + 업무활동과 규칙
  • 규칙을 명확하게, 구체화, 필요없는 것은 배제

감춰진 개념 추출하기

  • 선박화물 운송 예약을 위한 어플리케이션 도메인 모델

그림 1-8

public int makeBooking(Cargo cargo, Voyage voyage) {
  int confirmation = orderConfirmationSequence.next();
  voyage.addCargo(cargo, confirmation);
  return confirmation;
}
  • 요구사항 추가 10% 초과예약 허용

그림 1-9

public int makeBooking(Cargo cargo, Voyage voyage) {
  double maxBooking = voyage.capacity() * 1.1;
  if ((voyage.bookedCargosize() + cargo.size()) > maxBooking)
    return -1
  int confirmation = orderConfirmationSequence.next();
  voyage.addCargo(cargo, confirmation);
  return confirmation;
}
  • 10% 초과 예약 은 정책이고 이는 전략패턴으로 구조화 할 수 있다.

그림 1-10

public int makeBooking(Cargo cargo, Voyage voyage) {
  if (!overbookingPolicy.isAllowed(cargo, voyage)) return -1;
  int confirmation = orderConfirmationSequence.next();
  voyage.addCargo(cargo, confirmation);
  return confirmation;
}
public class overbookingPolicy {
    public boolean isAllowed(Cargo cargo, Voyage voyage) {
      return (cargo.size() + voyage.bookedCargoSize()) <= (voyage.capacity() * 1.1);
    }
}

도메인의 모든 세부사항에 이러한 정교한 설계를 적용하라고 권하는 것이 아니다.

  • 중요한 것에 집중 하고 나머지는 축소하거나 분리
  • 지식을 보전하고 공유
  • 명시적인 설계
  • 그리고 도메인 전문가와 프로그래머 사이에 피드백 고리 를 완성하는 것

심층 모델

더 깊은 지식탐구를 통한 도메인 모델링

본 포스트는 도메인 주도 설계 라는 책을 참조하였습니다. 가능하면 본 책을 직접 보시고 DDD를 이해하는 것을 추천드립니다. http://www.aladin.co.kr/shop/wproduct.aspx?ItemId=12174216

사용자가 프로그램을 사용하는 대상 영역이 바로 해당 소프트웨어의 도메인(Domain) 이다

  • 항공권 예약 프로그램 : 승객
  • 회계 프로그램 : 화폐, 금융
  • 도메인은 컴퓨터와 거의 관련이 없다

모델 은 지식을 선택적으로 단순화하고 의식적으로 구조화한 형태다.

  • 문제의 복잡성을 단순화

도메인모델

  • 다이어그램이 아니라 다이어그램이 전달하고자하는 아이디어
  • 도메인전문가의 머릿속에만 존재하는 지식이 아니라 해당 지식을 엄격하게 구성하고 선택적으로 추상화 한 것 - 개발을 위해서

도메인 주도 설계에서의 모델의 유용성

  1. 모델과 핵심 설계는 서로 영향을 주며 구체화된다.
  2. 모델은 모든 팀 구성원이 사용하는 언어의 중추다.
  3. 모델은 지식의 정수만을 뽑아낸 것이다.

소프트웨어의 본질

  • 기술에 집중하기 보다는 도메인 연구에 먼저 집중해야한다.
  • 도메인이 중심이 되는 개념을 바탕으로 프로젝트를 진행해야한다.
  • 복잡성을 단순화 시키는 명쾌한 모델 제시 - 통찰력

회사에서 새로운 맥북을 지급 받은 것을 바탕으로 포스팅을 함.

순전히 본인의 취향이 반영된 것임을 미리 알려드림.

App Store

Xcode (필수아님)

  • App Store : https://itunes.apple.com/kr/app/xcode/id497799835?mt=12

Xcode Command Line Tools

$ xcode-select --install

Alfred

  • Spotlight 대처
  • App Store : https://itunes.apple.com/kr/app/alfred/id405843582?mt=12

Evernote

  • App Store : https://itunes.apple.com/kr/app/evernote/id406056744?mt=12

PDF Reader X

  • App Store : https://itunes.apple.com/kr/app/pdf-reader-x/id684812309?mt=12

Microsoft Remote Desktop

  • App Store : https://itunes.apple.com/kr/app/microsoft-remote-desktop/id715768417?mt=12

Homebrew

  • OS X 용 패키지 관리자
  • http://brew.sh/index_ko.html
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrew extension Cask

  • Homebrew의 확장 저장소
  • 각종 retail 어플리케이션 지원
brew cask install google-chrome

Chrome

  • 웹브라우저
brew cask install google-chrome

iTerm

  • OS X 용 터미널 프로그램
brew cask install iterm2

Docker

  • 가상환경 컨테이너
  • virtualbox + docer2boot + docker 통합 설치
brew install cask dockertoolbox

Dev

Java SDK

brew cask install java

Oracle sqldeveloper

  • 오라클 클라이언트 툴
  • http://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html

MySQL Workbench

  • MySQL 클라이언트 툴
brew cask install mysqlworkbench

SourceTree

brew cask install sourcetree

IntelliJ

brew cask install intellij-idea

STS (eclipse)

brew cask install sts

Atom

  • Chromium 기반 에디터
brew cask install atom
  • markdown-writer 패키지 추가 : jekyll 관리용

StarUML

  • 무료 UML 툴
brew cask install staruml

Utils

Office

  • 적절한 오피스 프로그램 설치요망
OpenOffice : 무료
brew cask install openoffice
iWork : 신규 맥 또는 iOS 기기구입자 무료
  • Pages : https://itunes.apple.com/kr/app/pages/id409201541?mt=12
  • Numbers : https://itunes.apple.com/kr/app/numbers/id409203825?mt=12
  • Keynote : https://itunes.apple.com/kr/app/keynote/id409183694?mt=12
Microsoft Office

jekyll

  • 정적페이지 생성 툴
  • Github pages 등을 이용한 블로그 관리
sudo gem install jekyll

XMind

  • 무료 마인드맵 툴
brew cask install xmind

Skype

  • 영상통화
brew cask install skype

Shell

tree

  • 폴더 구조를 트리형태로 노출
brew install tree

Font

Source Code Pro

  • https://github.com/adobe-fonts/source-code-pro

네이버 나눔글꼴

  • http://hangeul.naver.com/font

Hack

  • https://github.com/chrissimpkins/Hack#desktop-usage

참조 : https://gist.github.com/kevinelliott/3135044

최근 각종 개발자 방송에 유행이다. 나프다, 코코티비 등 뿐만 아니라 웹비나도 같이 유행이다.

그런데 최근에 토비의 스프링을 저술하신 Toby님 께서 라이브코딩 방송을 하였다. 관련해서 해당 내용을 복기하는 차원에서 포스트를 해본다.

  • 간단하게 내용을 이야기 하자면 스프링 어플리케이션 초기화(자원할당), 마무리(자원해제) 에 대한 고찰이다.

토비님 방송 링크

Step 1. Simple

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) {
    SpringApplication.run(Springcl2Application.class, args);

    System.out.println("main()");
  }
}

단순하게 main()을 출력하고 마무리.

  • 참고로 @SpringBootApplication 는 각종 메타 어너테이션이 선언되어 있다.
  • 기본적으로 @Target(ElementType.TYPE) 으로 선언되어 있으면 ElementTypeANNOTATION_TYPE 에서도 사용할 수 있기 때문에 일반 어너테이션 이면서 메타 어너테이션이 될 수 있다.
  • 메타 어너테이션으로만 사용하고 싶으면 자바 기본 메타 어너테이션 처럼 @Target(ElementType.ANNOTATION_TYPE) 으로 선언하면 된다.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Configuration
@EnableAutoConfiguration
@ComponentScan
public @interface SpringBootApplication {
  ...
}

Step 2. Implementation CommandLineRunner

@SpringBootApplication
public class Springcl2Application implements CommandLineRunner {
  public static void main(String[] args) {
    SpringApplication.run(Springcl2Application.class, args);

    System.out.println("main()");
  }

  @Override
  public void run(String... args) throws Exception {
    System.out.println("run()");
  }
}
  1. run() 출력
  2. main() 출력

Step 3. CommandLineRunner @Bean 등록 with Lamda Expression

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) {
    SpringApplication.run(Springcl2Application.class, args);

    System.out.println("main()");
  }

  @Bean
  public CommandLineRunner runner() {
    return (a) -> {
      System.out.println("runner()");
    };
  }
}
  1. runner() 출력
  2. main() 출력

Step 4. CommandLineRunner @Component 등록

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) {
    SpringApplication.run(Springcl2Application.class, args);

    System.out.println("main()");
  }

  @Component
  public static class MyRunner implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
      System.out.println("MyRunner#run()");
    }
  }
}
  1. runnerMyRunner#run() 출력
  2. main() 출력

Step 5. InitializingBean @Bean 등록 with Lamda Expression

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) {
    SpringApplication.run(Springcl2Application.class, args);

    System.out.println("main()");
  }

    @Bean
    public InitializingBean runner() throws Exception {
      return () -> {
        System.out.println("afterPropertiesSet()");
      };
    }
}
  1. afterPropertiesSet() 출력
  2. main() 출력

Step 6. try-with-resources

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) {
    try (ConfigurableApplicationContext ac = SpringApplication.run(Springcl2Application.class, args)) {
      System.out.println("Something");
    }
    System.out.println("main()");
  }
}
  1. Something 출력
  2. main() 출력

Step 7. try-with-resources with Lamda Expression

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) throws Exception {
    ConfigurableApplicationContext ac = SpringApplication.run(Springcl2Application.class, args);
    try (AutoCloseable closable = () -> { System.out.println("Auto close"); })
      System.out.println("Something");
    }
    System.out.println("main()");
  }
}
  1. Something 출력
  2. Auto close 출력
  3. main() 출력

Step 8. Runtime.addShutdownHook

@SpringBootApplication
public class Springcl2Application {
  public static void main(String[] args) {
    Runtime.getRuntime().addShutdownHook(new Thread() {
      @Override
      public void run() {
        System.out.println("shutdown");
      }
    });
    ConfigurableApplicationContext ac = SpringApplication.run(Springcl2Application.class, args);
    try (AutoCloseable closable = () -> { System.out.println("Auto close"); }) {
      System.out.println("Something");
    }
    System.out.println("main()");
  }
}
  1. Something 출력
  2. Auto close 출력
  3. main() 출력
  4. shutdown 출력

Runtime.addShutdownHook 참조 링크

Conclusion

스프링의 라이프사이클 과 자바에서 제공하는 라이브러리 등을 이용해서 스프링 어플리케이션 구동 시 각종 초기화, 마무리 작업을 다양한 방법으로 제어할 수 있다.