마이크로 서비스로의 진화

- 기업은 소셜미디어, 모바일, 클라우드, 빅데이터, 사물인터넷드을 주요 수단으로 활용하며 시장에 빠르게 진입하기위한 방법에 관심을 갖게 되었으며, 그러한 것을 가능하게 하기위해 애자일성, 변경 및 배포 신속성, 확장성 같은 요소들에 관심을 갖게 되었다. 

- 기술적으로 다양한 관점에서 모든 계층의 구조를 다시 고려하게 하는 기술들이 개발 되었다. 프론트엔드의 Angular, Ember, Backbone등, 클라우드 환경의 AWS, 컨테이너 관련 Docker, 데이터베이스 영역에서는 NoSQL이 등장하였고  Hadoop, Cassandra, CochDB, Neo4j등이 있다.


마이크로서비스란 무엇인가?

- 물리적으로 분리할 수 있는 모듈화된 애플리케이션을 만들 수 있는 아키텍처 스타일이다.

- IT 시스템을 자율적이고, 자기 완비적(self-contained)이면서도 느슨하게 연결된 비즈니스 범위의 집합으로 만드는 아키텍처 스타일이다.

- 마이크로서비스 사이의 통신이나 전송방식에 정해진 표준은 없으나, 일반적으로 HTTP REST같은 경량프로토콜, AMQP같은 메시징 프로토콜을 주로 사용한다. 필요에 따라 Thrift, ZeroMQ, Protocol Buffers, Avro처럼 특수한 목적에 최적화된 통신프로토콜을 사용할 수도 있다.

- 마이크로서비스를 적용하기위해서 조직의 변화도 중요하다 기술에 대한 내재화, 개발과 운영의 간극을 좁히기 위한 DevOps같은 개념이 필연적이다


마이크로서비스의 원칙

- 서비스 하나에책임도 하나 : 클래스든 함수든 서비스든 간에 하나의 단위 요소는 반드시 하나의 책임만을 가져야 한다

- 마이크로 서비스는 자율적 : 자기 완비적이고, 독립적으로 배포할 수 있으며, 자율적인 서비스로서 비즈니스의 범위와 실행에 대해 전적인 책임을 진다.

- MSA 와 SOA의 차이점은 자율성 수준의 차이다. SOA는 서비스 수준의 추상화, MSA는 실행환경까지도 추상화


마이크로서비스의 특징

- 서비스는 일급 시민

- 서비스 계약

- 느슨한 결합

- 서비스 추상화

- 서비스 재사용

- 무상태

- 서비스 발견성

- 서비스 호환성

- 서비스 조립성

- 마이크로서비스는 경량

- 다양한 언어로 구성 가능

- 개발에서 배포까지 쉬운 자동화

- 마이크로서비스를 지원하는 생태계

- 동적이고 분산돼 있는 마이크로서비스

- 붕괴 저항성, 빨리 실패하기, 자체 치유


마이크로서비스의 장점

- 폴리글랏 아키텍처 지원 : 각 서비스는 자신만의 아키텍처와 여러가지 기술을 적용하여 구축 운영

- 실험과 혁신 유도

- 탄력적이고 선택적인 확장

- 대체 가능성

- 유기적 시스템 구축 유도 : 시간이 지남에 따라 점점 더 많은 기능을 추가하면서 성장해가는 시스템

- 기술적 부채 경감

- 다양한 버전의 공존

- 자기 조직화 시스템 구축 지원

- 이벤트 주도 아키텍처 지원

- 데브옵스 지원


다른 아키텍처 스타일과의 관계

SOA와의 관계 : SOA를 도입하는 방식에 따라 같기도 하고 다르기도 하다

- 서비스 지향 통합 : 다름

- 기존 시스템의 현행화 : 다름

- 서비스 지향 애플리케이션 : 마이크로서비스를 현재 도입한 SOA의 논리적인 다음 단계

12 요소 애플리케이션과의 관계

- 클라우드에서 운영 가능한 현대적인 애플리케이션에서 기대할 수 있는 특징을 기술하는 방법론

- 단일 코드 베이스 : 각 마이크로서비스는 자체적인 코드베이스를 가져야하고 다른 마이크로서비스와 공유되지 않는다(git/subversion)

- 의존성 꾸러미(bundling dependencies) : 모든 애플리케이션은 필요한 모든의존성을 애플리케이션과 함께 하나의 꾸러미에 담아야 한다(Maven/Gradle)

-  환경설정 외부화(externalizing configurations) 

- 후방 지원 서비스 접근성(backing service)

- 빌드, 출시, 운영의 격리

- 무상태, 비공유 프로세스

- 서비스를 포트에 바인딩해서 노출

- 확장을 위한 동시성 : 프로세스가 복제를 통해 확장될 수 있게 설계

- 폐기 영향 최소화(disposability with minimal overhead) : 애플리케이션의 시동과 종료에 필요한 시간을 최소화하고, 서버가 종료될때에는 필요한 처리가 모두 수행되어야 함(lazy loading)

- 개발과 운영의 짝 맞춤(Development and production parity) : 개발 환경과 운영 환경을 가능한 한 동일하게 유지

- 로그 외부화 : 로그파일을 절대로 자기 자신안에 담지 않는다. 중앙집중식 로깅 프레임워크사용(Splunk, Greylog, Logstash, Logplex, Loggly) 로그백 추가자를 통해 로그를 중앙 저장소에 적재하고, 적재 도구의 종단점에 로그를 쓰는 방법

- 패키지 관리자 프로세스 : 관리자 태스크를 위한 코드도 애플리케이션 코드와 함께 패키징되어야 한다.


마이크로서비스 사용 사례

- 일체형의 전환

- 넷플릭스, 우버, 에어비앤비, 오비츠, 이베이, 아마존, 길트, 트위터, 나이키


참고자료

- hexagonal architecture : http://alistair.cockburn.us/Hexagonal+architecture

- avro : http://avro.apache.org/docs/current/

- DevOps : http://dev2ops.org/2010/02/what-is-devops/

- 일급시민 : https://ko.wikipedia.org/wiki/일급_객체

2013년인가 14년도 즈음에 프로젝트에서 회고를 진행하였고 급 관심이 생겨 애자일회고라는 책을 샀다. 그때는 책을 사서 한번 읽어보고 재미는 있겠는데 현실적이진 않다고 생각했었다. 


얼마전 회고를 진행하게 되어 다시 책을 읽어봤고, 처음 읽었을때와는 다르게 활동하나하나가 재밌고 유익하게 느껴졌다. 


책의 진행방식에 따라 아래와 같은 순서로 회고를 진행해 보았다.


1. 사전준비

- 처음으로 회고를 진행하는 나와 팀원들의 어색함을 달래주기 위해 체크인 활동을 했다

2. 자료모으기

- 예상치 못한 팀의 변고로 인해 스프린트가 길어졌고 긴 스프린트를 돌이켜 보기위해 시간축 활동을 했다

3. 통찰 이끌어내기

- 브레인 스토밍학습매트릭스 활동을 하려했으나 시간 부족 및 진행 미숙으로 브레인 스토밍만 진행했다

4. 무엇을 할지 결정하기

- 스마트 목표 활동을 하려 했으나 역시 시간부족과 진행 미숙으로 브레인스토밍과 액션아이템을 뽑는 작업이 같이 이루어 졌다

5. 회고 끝내기 

- 끝낼때는 감사표현하기 활동으로 돌아가며 감사의 말을 하려고 했으나 어색함속에 간단한 형식적인 감사의 말로 급하게 마무리 되었다


첫 회고 진행 이어서 미숙하고 각 활동의 목표를 달성하지는 못했지만 액션아이템도 뽑고 모두 웃으며 지루하지 않고 재미있는 회고 였던것 같다


  • 모델링 과정을 매우 복잡한 도메인까지 확장하는 원리
  • 성공적인 모델 : 규모와는 상관없이 모순되거나 정의가 겹치지 않고 처음부터 끝까지 논리적인 일관성 유지
  • Bounded Context 
  • Distillation
  • 대규모 구조 : 전혀 공통점이 없는 부분들이 맞물려 돌아가도록 일관성을 가져다 줌
    • Responsibility Layer
    • Evolving Order(발전하는 질서)
모델의 무결성 유지
  • 각 모델이 적용되는 경계를 분명하게 합의하는 것에서 출발
  • 단일화(unification) : 각 용어가 모호하지 않고 모순되는 규칙이 없는 모델의 내적 일관성
  • 다른 여러 모델 간의 경계와 관계를 표시해줄 수단 필요
    • Context Map : 프로젝트 분야를 매핑, 각 컨텍스트 간의 관계의 전체적인 개관
    • Bounded Context : 각 모델의 적용가능성의 범위 정의
    • Continuous Integration : 프로세스를 토대로 모델의 단일화 유지
    • Shared Kernel
    • Separate Ways
Bounded Context
  • 규모가 큰 프로젝트에는 다수의 모델이 공존하며, 서로 다른 모델은 서로 다른 컨텍스트에 적용된다
  • 한 팀 내에서도 다수의 모델이 공존할 수 있다
  • 다수의 모델 탓에 발생하는 문제의 해결 방법
    • 소프트웨어 내의 제한된 부분으로 특정 모델의 범위를 명확하게 정의
    • 팀의 구성과 조화
    • 컨텍스트의 경계를 팀 조직, 애플리케이션의 특정 부분에서의 사용법, 코드 기반이나 데이터베이스스키마와 같은 물리적인 형태의 관점에서 명시적으로 설정
    • 경계 내에서는 모델을 엄격하게 일관된 상태로 유지하고 경계 바깥의 이슈 때문에 초점이 흐려지거나 혼란스러워져서는 안된다
    • 모델을 책임지는 팀에서는 영속화를 비롯한 각 객체의 전체 생명주기를 다룬다

Bounded Context안의 균열 인식

  • 코드로 작성된 인터페이스가 서로 맞지 않는 경우
  • 예상치 못한 행위
  • 언어를 혼동한 상태로 구사
  • 중복개념 : 실제로 같은 개념을 나타내는 두개의 모델 요소가 존재하는 것
  • 허위동족언어(false cognates) : 같은 용어를 사용하는 두 사람이 서로 같은 것을 이야기하고 있다고 생각하지만 실제로는 그렇지 않은 경우, 개념상의 충돌
  • 해결방안
    • Continuous Integration
    • Context Map
    • Shared Kernel
    • Customer/Supplier Development Team
    • Conformist
    • Anticorruption Layer
    • Separate Ways
    • Open Host Service
    • Published Language

Continuous Integration

  • 내부적으로 균열이 발생할 때 이를 빠르게 포착하고 정정할 수 있을 정도로 컨텍스트 내의 모든 작업을 빈번하게 병합해서 일관성 유지
  • 모델 개념의 통합
    • 팀 구성원 간의 부단한 의사소통을 토대로 통합
  • 구현 수준에서의 통합
    • 모델내의 균열을 조기에 드러내는 체계적인 병합/빌드/테스트 프로세스를 토대로 통합
  • 프로세스
    • 단계적이고 재생 가능한 병합/빌드 기법    
    • 자동화된 테스트 스위트
    • 수정사항이 통합되지 않은 상태로 존재할 수 있는 시간을 적당히 짧게 유지하는 규칙

Context Map

  • Bounded Context간에 코드를 재사용하는 것음 위험하므로 기능과 데이터는 번역 과정을 거쳐 통합해야 한다.
  • 서로다른 컨텍스트간의 관계를 정의하고 프로젝트상의 모든 모델 컨텍스트를 아우르는 전체적인 뷰를 만들면 혼란을 줄일 수 있다
  • Context Map은 프로젝트 관리와 소프트웨어 설계 영역 사이에 걸쳐있는 개념
  • 의사소통을 위해 컨텍스트 간의 번역에 대한 윤곽을 명확하게 표현
  • 컨텍스트 간에 공유해야 하는 정보를 강조
  • 모델과 모델이 만나는 경계 지점 서술
  • Bounded Context간의 관계 패턴
    • Shared Kernel
    • Customer/Supplier(현재 가장 많이 적용되어 있는 패턴)
    • Separate Ways
    • Open Host Service
    • Anticorruption Layer(wrapper, translator, facade, service)

디스틸레이션

  • 혼합된 요소를 분리해서 본질을 좀더 값지고 유용한 형태로 뽑아내는 과정
  • Generic subdomain - 모델에서 가장 특색이 없는 측면 제거
  • Coherent Mechanism - 용도가 다양하고, 의미전달이 용이하며, 유연한 설계를 통해 캡슐화
  • Segregated Core
  • Abstract Core - 가장 근본적인 개념과 관계를 순수한 형태로 표현
  • Core Domain
    • Domain Vision Statement - 최소한의 투자로 기본개념과 가치를 
    • Highlighted Core - 의사소통을 향상시키고 의사결정에 도움

Core Domain

  • 도메인 모델을 가치있는 자산으로 만들려면 모델의 핵심적인 측면을 다루기 수월해야 하고 애플리케이션의 기능성을 만들어내는 데 충분히 활용할 수 있어야 한다
  • Core Domain은 시스템에서 가장 큰 가치가 더해지는 곳이다
  • 시스템의 비전을 수행하기에 충분한 심층 모델을 찾고 유연한 설계를 개발할 수 있게 코어에 노력을 쏟아라

Generic Subdomain

  • 모델은 일반 원칙(누구나 알고 있는것)이나 세부사항(보조적인 역할을 수행하는 전문 분야에 속하는 것) 탓에 정체된다
  • 개발 시 선택사항
    • 기성 솔루션(상용 또는 오픈소스)
    • 공표된 설계나 모델
    • 외주 제작
    • 사내구현
  • 일반화가 재사용 가능하다는 의미는 아니다
  • 필요한 만큼만 투자
  • 쉽기 때문에 보조 시스템을 먼저 구축할 경우 위험관리의 목적을 헛되게 할 수 있음

Domain vision Statement

  • 도메인 모델의 본질과 해당 도메인 모델이 얼마나 기업에 가치 있는가에 초점을 맞춘다
  • 팀 내에서 방향성을 공유하게 해 줌
  • 작성방법
    • Core Domain을 짧게 기술하고 그것이 가져올 가치에 해당하는 가치제안을 작성
    • 이 도메인 모델과 다른것과 구별하는데 도움되지 않는 측면은 무시
    • 도메인 모델이 어떻게 다양한 관심사를 충족하고 균형을 이루는지 보여라
    • 한정된 범위에서 내용을 유지하라
    • 초기에 이 선언문을 작성하고 새로운 통찰력을 얻을 때마다 선언문을 개정하라


Highlighted Core

  • 어떤 모델에서 특별히 중요한 부분을 그것을 구체화한 구현과 함께 표시할 필요가 있는데, 이러한 표시는 모델에 대한 설명이지 반드시 모델 자체의 한 부분일 필요는 없다
  • 기법
    • 디스틸레이션 문서 : Core Domain과 Core의 구성요소 사이에서 일어나는 상호작용을 기술하는(3에서 7페이지 가량의) 매우 간결한 문서를 작성하라
    • 표시된 Core : 모델의 주요 저장소 안에 있는 Core Domain의 구성요소에 대해 그것의 역할을 설명하려하지 말고 표시하라. 개발자가 힘들이지 않고도 Core의 안과 밖을 알 수 있게 하라

Cohesive Mechanism

  • 의도를 드러내는 이름이 이정된 메서드에 복잡한 알고리즘을 숨기면 "무엇"이 "어떻게"와 분리된다
  • 문제해결을 위한 알고리즘을 제공하는 수많은 메서드가 문제를 표현하는 메서드를 분분명하게 만드는 경우
    • 개념적으로 Cohesive Mechanism을 별도의 경량 프레임워크로 분할
    • Intention-Revealing Interface로 프레임워크의 기능을 노출
    • 해법의 복잡성("어떻게")을 프레임워크에 위임해서 문제("무엇")을 표현하는데 집중    

Segregated Core

  • 설계자가 가장 중요한 관계를 분명하게 볼 수 없다면 취약한 설계로 이어지는 결과가 나타난다
  • 모든 일반적이거나 보조적인 역할을 하는 구성요소를 다른 객체로 추출해서 다른 패키지에 배치하라
Abstract Core
  • 모델의 가장 근본적인 개념을 식별해서 그것을 별도의 클래스나 추상 클래스, 또는 인터페이스로 추출하라
  • 이 추상 모델이 중요 컴포넌트 간에 발생하는 상호작용을 대부분 표현할 수 있게끔 설계하라
  • 특화되고 세부적인 구현클래스는 하위 도메인을 기준으로 정의된 자체적인 모듈에 남겨둔 상태에서 이 추상적이면서 전체적인 모델을 자체적인 모듈에 배치하라

리팩토링의 대상선택

  • 문제의 근원에 Core Domain이나, Core 와 지원요소와의 관계가 있는지 살핀다. 그렇다면 가장먼저 고쳐야 한다
  • 마음껏 리팩토링할 수 있는 상황이라면 제일먼저 Core Domain을 더 잘 분해하고 Core의 격리를 개선하며, 보조적인 하위 도메인이 Generic하게 만드는 데 집중한다.


리팩터링은 엔트로피와의 싸움이며 레거시 시스템이 퇴보하는 것을 막는 최전선에 놓여 있다.


암시적인 개념을 명확하게

  • 도메인 전문가가 사용하는 언어에 귀 기울여라
    • 어색한 부분을 조사하라
    • 설명하기 힘들만큼 복잡한 작업을 수행하는 프로시저와 관련된 부분이나 새로운 요구사항 탓에 복잡성이 증가하는 부분
    • 모순점에 대해 깊이 고민하라
  • 서적을 참고하라
  • 시도하고 또 시도하라
    • 모델러/설계자는 자신의 아이디어에 집착해서는 안된다
다소 불명확한 개념을 모델링하는 법
1. 명시적인 제약조건
  • 제약조건을 별도의 메서드로 분리하고 제약조건의 의미를 분명하고 명확하게 표현할 수 있게 메서드의 이름을 짓는다
  • 부여된 이름을 사용해서 제약조건에 관한 토의가 가능
  • 잘못된 제약조건 설계의 식별
    • 제약조건을 평가하려면 해당 객체의 정의에 적합하지 않은 데이터가 필요하다
    • 관련된 규칙이 여러 객체에 걸쳐 나타나며, 동일한 계층구조에 속하지 않는 객체 간에 중복 또는 상속 관계를 강요한다
    • 설계와 요구사항에 관한 다양한 논의는 제약조건에 초점을 맞춰 이뤄지지만 정작 구현단계에서는 절차적인 코드에 묻혀 명시적으로 표현되지 않는다.
2. 도메인 객체로서의 프로세스
  • 객체는 절차를 캡슐화해서 절차 대신 객체의 목표 의도에 관해 생각하게 해야 한다
  • 어떤 프로세스를 선택할 것인가는 곧 어떤 객체를 선택할 것인가가 되고, 각 객체는 각기 다른 Strategy를 표현한다

3. 명세(Specification)

  • 어떤 객체가 특정기준을 만족하는지 판단하는 술어다.
  • 다른객체에 대한 제약조건을 기술하며, 제약조건은 존재할 수도 존재하지 않을 수도 있다
  • 특별한 목적을 위해 술어와 유사한 명시적인 Value Object를 만들어라

유연한 설계

  • 너무 과도한 추상 계층과 간접 계층이 존재하면 오히려 유연성에 방해가 된다
  • 정교한 시스템을 목적으로 조립가능하고 이해하기 어렵지 않은 요소를 만들어내려면 적당한 수준의 엄밀한 설계형식과 접목하고자 노력해야 한다

1. 의도를 드러내는 인터페이스(Intention-Revealing Interface)

  • 캡슐화로 클라이언트 코드는 단순해지고 상위 수준의 개념 관점에서 코드를 이해할 수 있다
  • 인터페이스를 구성하는 각 요소(타입, 메서드, 인자 이름)의 이름을 토대로 설계 의도를 드러낸다
  • 결과와 목적만을 표현하도록 클래스와 연산의 이름을 부여(Ubiquitous Language 기반)
  • 행위에 대한 테스트를 먼저 작성
  • 방법이 아닌 의도를 표현하는 추상적인 인터페이스위로 모든 까다로운 메커니즘을 캡슐화
2. 부수효과가 없는 함수(Side-Effect-Free-Function)
  • 부수효과를 일으키지 않으면서 결과를 반환하는 연산을 함수라 한다
  • 함수는 여러번 호출해도 무방하며 매번 동일한 값을 반환한다
  • 명령과 질의를 엄격하게 분리 - 변경을 발생시키는 메서드는 도메인 데이터를 반환하지 않아야하고 가능한 단순하게 유지해야 한다
  • 명령과 질의를 분리하는 대신 연산의 결과를 표현하는 새로운 Value Object를 생성해서 반환
  • 상태변경을 수반하는 로직과 계산이 혼합된 연산은 리팩터링을 거쳐 두개의 연산으로 분리해야 한다
  • 복잡한 계산을 처리하는 책임을 Value Object로 옮긴다

3. 단언(Assertion)

  • 연산의 사후 조건과 클래스 및 Aggregate의 불변식을 명시하라
  • 개발자들이 의도된 단언을 추측할 수 있게 인도하고, 쉽게 배울수 있고 모순된 코드를 작성하는 위험을 줄이는 응집도 높은 개념이 포함된 모델을 만들려고 노력하라
4. 개념적 윤곽(Conceptuel Contour)
  • 이 개념이 현재 모델과 코드에 포함된 관계를 기준으로 했을때 적절한가, 또는 현재 기반을 이루는 도메인과 유사한 윤곽을 나타내는가?
  • 변경되는 부분과 변경되지 않는 부분을 나누는 중심 축을 식별하고, 변경을 분리하기 위한 패턴을 명확하게 표현하는 개념적 윤곽을 찾아라
  • 객체와 메서드를 와해시킬 정도로 광범위한 변경을 야기하는 요구사항이 나타났다는 것은 도메인에 관해 알고 있는 지식을 개선해야 한다는 메시지다
4. 독립형 클래스(Standalone Class)
  • Module과 Aggregate 모두 지나치게 얽히고 설키는 상호의존성을 방지하는 것이 목적이다
  • 명시적인 참조에 비해 암시적인 개념이 훨씬 더 많은 정신적 과부하를 초래한다.
  • 현재 상황과 무관한 모든 개념을 제거하라
  • 클래스가 완전히 독립적으로 바뀌고 단독으로 검토하고 이해할 수 있을 것이다
  • 모든 비본질적인 의존성을 제거하는 것이 목표다
  • 가장 복잡다단한 계산을 Standalone class로 도출하려고 노력하라
  • Standalone Class는 극단적으로 결합도를 낮춘 것이다

5. 연산의 닫힘(Closure of Operation)

  • 구현자(implementer)가 연산에 사용되는 상태를 포함하고 있다면 연산의 인자로 구현자를 사용하는 것이 효과적이므로 인자의 타입과 반환 타입을 구현자의 타입과 동일하게 정의한다. 이런 방식으로 정의된 연산은 해당 타입의 ㅇ니스턴스 집합에 닫혀 있다. 닫힌 연산은 부차적인 개념을 사용하지 않고도 고수준의 인터페이스를 제공한다.
  • 일반적으로 Entity는 어떤 계산의 수행결과를 표현하는 개념이 아니다. 따라서 대부분의 경우 Value Object에서 이 패턴을 적용할 기회를 찾을 수 있다.
6. 선언적 설계
  • 일반적으로 일종의 실행 가능한 명세(executable specification)로서 프로그램 전체 혹은 프로그램의 일부를 작성하는 방식
  • 특성(properties)을 매우 정확하게 기술함으로써 소프트웨어를 제어하는 것
  • 선언적인 프로그램의 이점을 누리려면 모든 개발자가 프레임워크의 규칙을 준수 해야 한다
  • 아주 좁은 범위로 한정된 프레임워크를 사용해서 영속성과 객체관계형 매핑과 같이 매우 지루하고 오류가 발생하기 쉬운 설계 측면을 자동화한 경우에 큰 가치를 얻었다
  • 규칙기반(rule base)
    • 원칙적으로는 선언적이지만 대부분의 시스템은 성능 최적화를 위해 제어술어(control predicate)를 포함
    • 이러한 제어코드는 부수효과를 수반하므로 더는 선언된 규칙만으로는 완전한 행위를 지시할 수 없다
7. 도메인 특화 언어
  • 특정 도메인을 위해 구축된 특정 모델에 맞게 조정된 프로그래밍 언어를 사용해 클라이언트 코드 작성
  • 프로그램의 표현력을 월등히 향상시킬 수 있음
  • Ubiquitous Language와도 가장 높은 일관성을 유지 할 수 있음
8. 하위 도메인으로 분할하라
  • 조금씩 뜯어내어 접근
  • 모델의 일부 영역이 전문적인 영역이거나, 상태변경에 제약을 가하는 복잡한 규칙을 적용한다면 끄집어내어 별도의 모델이나 규칙을 선언적으로 표현해주는 간단한 프레임워크 내부로 옮긴다
  • 하나의 영역에 집중

9. 가능하다면 정립된 정형화를 활용하라

  • 오랜 시간 동안 정립되어 온 개념적인 체계 이용
  • 수학적인 개념 - 도메인에 적절히 특화된 수학은 깔끔한 동시에 명확한 규칙과 결합할 수 있어서 사람들이 이해하기도 쉽다
모델과 디자인 패턴의 연결
  • 기술저인 문제에 대한 해법뿐 아니라 개념적인 도메인에 관한 해법도 제공해야 한다는 것이 디자인 패턴을 도메인 패턴으로 적용하기 위한 유일한 요구사항이다
1. Strategy(전략, 정책 패턴)
  • 여러프로세스의 선택이라는 문제를 해결
  • 프로세스의 규칙과 프로세스를 제어하는 행위를 서로 분리
  • 도메인 패턴으로 사용하는 관점에서는 프로세스 또는 정책적인 규칙과 같은 하나의 개념을 표현하는 능력에 중점

2. Composite

  • Composite내부에 포함된 모든 구성요소를 포괄하는 추상 타입을 정의
  • 계층구조상의 모든 수준에서 동일한 행위를 제공
  • 크고 작은 각 부분이 전체 구조를 투명하게 반영


Layered Architecture

  • SoC를 아키텍처 레벨에서 가능하게 해주는 패턴
  • 계층을 나누어 각 계층의 관심사에 집중할 수 있도록 한다.
  • 일반적으로 사용자 인터페이스, 응용, 도메인, 인프라스트럭쳐 계층이 있다.


도메인 모델 빌딩블럭

엔티티(Entity)

  • 어떤 객체를 일차적으로 해당 객체의 식별성으로 정의
  • 객체의 생명주기 내내 이어지는 추상적인 연속성
  • 엔티티의 속성 보다는 정체성에 초점


Value Object

  • 개념적 식별성을 갖지 않으면서 도메인의 서술적 측면을 나타내는 객체
  • 모델에 포함된 어떤 요소의 속성에만 관심
  • 불변적
  • 객체의 수가 많아질 수 있으므로 객체를 고유하여 최적화 가능


Service

  • 모델에서 독립적인 인터페이스로 제공되는 연산
  • 다른 객체와의 관계를 강조
  • 연산의 명칭은 Ubiquitous language에 도입되어야 함
  • 매개변수와 결과는 도메인 객체여야 함
  • 도메인 계층에서만 이용되는 것은 아님
  • 각 계층의 서비스와 도메인 서비스를 적절하게 나누는 것이 중요 함


Module

  • 패키지
  • 일련의 응집력있는 개념
  • 모듈의 이름은 도메인에 통찰력을 줄 수 있어야 함


도메인 객체의 생명주기


도메인객체의 관리 문제

  • 생명주기 동안의 무결성 유지하기
  • 생명주기 관리의 복잡성으로 모델이 난해해지는 것을 방지하기

해결방법

Aggregate

  • 소유권과 경계를 명확히 정의하여 객체 간의 연관관계가 복잡해지지 않도록 한다
  • 도메인 객체의 무결성 유지에 중요 함
  • 생명주기의 전 단계에서 불변식이 유지돼야 할 범위를 표시

Factory

  • 생명주기의 초기단계
  • 복잡한 객체와 Aggregate를 생성하고 재구성

Repository

  • 생명주기의 중간과 마지막
  • 영속 객체를 찾아 조회하는 수단
Aggregate
  • root와 boundary
  • boundary : 무엇이 포함되고 무엇이 포함되지 않는지 정의
  • root : 단 하나만 존재, 특정 엔티티
  • 경계 바깥의 객체는 해당 Aggregate의 구성요소 가운데 root 만 참조 가능
  • 불변식 : 데이터가 변경될 때마다 유지돼야 하는 일관성 규칙
  • 구현 규칙
    • 루트 엔티티는 전역 식별성을 지니며 불변식을 검사할 책임이 있다
    • 루트는 Value Object의 복사본을 단른 객체에 전달해 줄 수 있다
    • 삭제 연산은 경계안의 모든 요소를 한번에 제거해야 한다.
    • 변경시 전체 불변식은 반드시 지켜져야 한다




전역식별성

지역식별성

모델

  1. 중요한 사실이나 사상의 일부 측면을 나타냄 
  2. 대상을 단순화 한것
  3. 당면한 문제와 관련된 것을 추상화

도메인

  • 사용자가 프로그램을 사용하는 대상 영역
  • 컴퓨터와 거의 관련이 없음
도메인 모델
  • 특정한 다이어 그램이 아니라 전달하고자 하는 아이디어
  • 지식을 엄격하게 구성하고 선택적으로 추상화한 것
  • 모델과 핵심 설계는 서로 영향을 주며 구체화 된다
  • 모델은 모든 팀 구성원이 사용하는 언어의 중추
  • 모델은 지식의 정수만을 뽑아낸 것이다
Ubiquitous language
  • 클래스, 주요 연산등의 이름
  • 명시적으로 드러나는 규칙을 토론하기 위한 용어
  • 도메인 모델에 적용하는 패턴의 이름
  • 공통언어(도메인 전문가와 개발팀간)로서 모델과 코드에서 사용해야 한다.
  • 모델, 설계, 구현 전체를 연결하는 핵심 키

도메인 모델 작성시 가장 중요하다고 생각하는 것은 추상화이다. 특히 기존 코드를 분석하여 모델을 만드는 경우 코드의 세세한 부분까지 모델에 표현하려는 욕심은 자제해야 한다.



1. 프로시저를 써서 요약하는 방법

1. LISP를 사용하는 이유

- 프로그램을 짜는데 필요한 여러 원소와 갖가지 데이터 구조를 공부하고, 이를 뒷받침하는 데 어떤 언어 기능이 있어야 하는지를 관계지어 설명하기에 알맞은 점이 많다


1.1 프로그램을 짤때 바탕이 되는 것

- 프로그래밍 언어는 프로세스에 대한 사람의 생각을 짜임새 있게 담아내는 그릇

- 프로그래밍의 구성 요소

  - 프로시저 : 데이터를 처리하는 규칙을 적은 것

  - 데이터 : 프로시저에서 쓰려는 물건(stuff)

- 좋은 프로그래밍 언어의 세가지 표현 방식

  - 기본식

  - 엮어내는 수단(means of combination) : 간단한것을 모아 복잡한것으로 만든다

  - 요약하는 수단(means of abstraction) : 복잡한 것에 이름을 붙여 하나로 다룰 수 있게끔 간추린다

  --> 기본 데이터와 기본 프로시저를 나타내고, 프로시저들과 데이터들을 엮어서 더 복잡한 것을 만들고 이를 간단하게 요약하는 수단이 반드시 있어야 함


 1.1.1 식(expression)

- 셈(evaluation)

- 엮은식(combination) : 여러식을 괄호로 묶어 리스트를 만들고 프로시저 적용(procedure application)을 하도록 엮어 놓은 식

- 연산자(operator), 피연산자(operand), 인자(argument)

- 앞가지 쓰기(prefix notation) : 연산자를 피연산자 왼쪽에 두는 방식

  - 장점 1 : 인자가 많아져도 따로 문법을 만들 필요 없이 쓰던 그대로 쓸수 있다

  - 장점 2 : 식속에 다시 식을 넣어서 식을 여러 겹으로 엮어 늘리기가 쉽다

- 가지런히 쓰기(pretty-printing) : 식을 깊이 겹쳐 써야 할 때 인자를 중심으로 줄을 맞추고 알맞게 들여 쓰는 방식

- read-eval-print loop


1.1.2 이름과 환경

- 변수 : 계산하는 물체(computational object)에 붙이는 이름

- define : 이름을 붙이는 방법(scheme)

  - 합친연산(compound operation)을 간추리는 수단 

- 실행기(interpreter)구성

  - 메모리 : 이름-물체의 쌍을 저장 --> 환경


1.1.3 엮은식(combination)을 계산하는 방법

- 엮은식의 값을 계산하는 차례

  1. 엮은식에서 부분 식(subexpression)의 값을 모두 구한다

  2. 엮은식에서 맨 왼쪽에 있는 식(연산자)의 값은 프로시저가 되고, 나머지 식(피연산자)의 값은 인자가 된다. 프로시저를 인자에 적용하여 엮은식의 값을 구한다

  --> 되도는(recursive)프로세스

- 나무꼴 어큐뮬레이션(tree accumulation)

  - node : 연산자

  - branch : 연산할 것들


1.1.4 묶음 프로시저(compound procedure)

- 프로시저 정의 : 복잡한 연산에 이름을 붙여서 쓰는 방법

  - (define (<name> <formal paramegters>) <body>)

  - <name> : 환경에서 프로시저를 가리키는 이름

  - <formal parameters> : 프로시저가 받아오는 인자를 가리키기 위해서 프로시저의 몸속에서 쓰는 이름

  - <body> : 프로시저를 불러 쓸 때마다 계산할 식


1.1.5 맞바꿈 계산법(substitution model)으로 프로시저를 실행하는 방법

- 묶음 프로시저를 인자에 맞춘다는것은 프로시저의 몸 속에 있는 모든 인자 이름(formal parameter)을 저마다 그에 대응하는 인자 값으로 맞바꾼 다음, 그렇게 얻어낸 식의 값을 구하는 것이다

- 샘플 (f 5)

(define (square x) (* x x))


(define (sum-of-squares x y)

 (+ (square x) (square y)))


(define (f a)

 (sum-of-squares (+ a 1) (* a 2)))

1. a -> 5로 맞바꿈 : (sum-of-square (+ 5 1) (* 5 2)) --> 연산자 : sum-of-square, 피연산자 : (+ 5 1), (* 5 2)

2. sum-of-squares x y -> x=6, y=10으로 맞바꿈 : (+ (square 6) (square 10)) --> 연산자 : + , 피연산자 : (square 6), (square 10)

3. square x -> x = 6 , x= 10 두개 식 맞바꿈 : (+ (* 6 6) (* 10 10))

4. (+ 36 100)

5. 136

- 실제 실행기가 돌아가는 방법은 아니며, 식의 계산 과정(evaluation process)에 대하여 제대로 형식을 갖추고 생각을 정리하려는 출발점일 뿐

- 실제로는 갇힌환경(local environment)에 인자 이름을 넣어놓고 계산하는 방법을 사용함

- 인자먼저 계산법(applicative order) : LISP 실행기 적용 방식

  - 인자값 먼저 구하는 방법

  - 같은식을 여러번 되풀이 계산하는 경우가 생기지 않아 좀 더 빠름, 정의 대로 계산하는 규칙이 더 복잡함


- 정의대로 계산법(normal order)

  - 인자값을 계산하지 않고 식 자체를 인자이름으로 맞바꾸어 가다가 마지막에 기본연산으로만 이루어진 식을 얻을때 값을 구하는 방법

  - 끝까지 펼친 다음에 줄이는 계산 방법

  (f 5)

  1. (sum-of-square (+ 5 1) (* 5 2))

  2. (+ (square (+ 5 1) (square (* 5 2))))

  3. (+ (* (+5 1) (+5 1)) (* (* 5 2) (*5 2))) : (+ 5 1), (* 5 2)를 두번 계산함

  4. (+ (* 6 6) (* 10 10))

  5. (+ 36 100)

  6. 136


1.1.6 조건 식과 술어(predicate)

- 조건식 문법(cond)

  (cond (<p1> <e1>)

        (<p2> <e2>)

        ...

        (<pn> <en>)

        (else <e>)

  - 절(clause) : 두식을 괄호로 묶어 놓음 (<p1> <e1>)

  - 술어(predicate) : 참/거짓

- 조건식 문법(if)

  (if <predicate> <consequent> <alternative>)

- 논리연산(and, or, not)

  (and <e1> ... <en>) : 왼쪽에서 오른쪽으로 계산, 그 가운데 거짓이라 대답하는 <e>가 나오면 and 값은 거짓이 되고 나머지<e>는 구하지 않음. 모든<e>가 참일 때에는 마지막 식의 값을 반환

  (or <e1> ... <en>) : 왼쪽에서 오른쪽으로 계산, 그 가운데 참이라 답하는 <e>가 나오면 참이되고, 나머지는 구하지 않음. 모든<e>가 거짓일 때에는 마지막 식의 값을 반환

  (not <e>) : <e>가 참이면 거짓, 거짓이면 참


1.1.7  연습 : 뉴튼 법으로 제곱근 찾기

- 함수와 프로시저 차이

  - 프로시저는 효율성을 갖춰야 함

  - 함수 : 무엇이 어떤 성질을 지니는지 밝힘

  - 프로시저 : 무엇을 어떻게 만들지/구할지 나타내는 일

- 뉴튼법 : 얻고자 하는 값에 가까운 값을 차례로 되풀이해서 구해 나감


1.1.8 블랙박스처럼 간추린 프로시저

- 프로시저 요약하기 : 프로시저를 묶어서 간추려 놓은 이름

- Block Structure : 프로시저 정의를 겹쳐쓰는 모양

- lexical scoping 규칙 : 문범에 따라 변수가 보이는 넓이가 정해지는 규칙



환경

- jdk6

- Spring + CXF 

- Weblogic 10.3.x

문제

WAS배포후 로딩시 cxf.config0 빈을 찾을 수 없다는 예외가 발생함.

라이브러리 충돌 문제로 추정

해결

xcersimpl 버전이 낮아 XSD1.1표준을 만족하지 않아 발생하는 문제.

xecersimpl 버전을 올려 해결. 현재 최신버전은 2.11.0 버전임.

참고

http://marlowa.blogspot.kr/2009/08/how-to-solve-weird-jaxbxerces-spring.html

https://xerces.apache.org/xerces2-j/index.html



맥북 관련

맥북hosts파일 위치 : /private/etc/hosts

brew 인스톨 경로 : /usr/local/Cellar

brew를 이용하여 설치한 정보 : brew info 프로그램명

mysql db 생성 : /usr/local/Cellar/mysql/5.6.23/scripts/mysql_install_db --user=mysql --basedir=/usr/local/Cellar/mysql/5.6.23 --datadir=/usr/local/Cellar/mysql/5.6.23/data

mysql 시작 : sudo /usr/local/Cellar/mysql/5.6.23/support_files/mysql.server start, mysql.server start

Homebrew는 심볼릭링크를 자동으로 생성해준다 ㅎㅎ

설치경로 찾기 : which 

intellij 관

command + shift + a : 단축키 검색

sysout : sout/so tab

구현체로 이동 : command + option + b

이동 : command + option + 방향키

라인이동 : shift + option + 방향키

유틸리티 클래스를 설계하면서의 고민..
1. 네이밍
2. 구조
3. as-is와의 호환?

유틸리티 클래스에 대해서는 설계적인 고민을 전혀 하지 않았다. 어차피 static메소드 자체가 OO와는 거리가 있다는 생각에 그냥 필요한 메소드를 쭉 구현만하면 되지 않을까라는 생각을 가지고 있었다. 이런식의 생각은 매우 위험하다. 특히 어차피 라는 말.. 어차피가 들어가기 시작하면 설계고 구현이고 대충 하게 되도 어차피라는 말로 전부 커버가 되버린다. 비록 static 메소드만을 가지고 있는 유틸리티 클래스 일지라도 그 범위내에서 고민할 내용들은 무지 많아진다 특히 프레임웍 개발이나 공통 패키지의 작업일 경우는 더욱 그러하다.

네이밍이 엉망일 경우 나같아도 그기능을 사용하지 않는다. 네이밍을 보고 일단 무시 들어가 버린다. 근데 내가 그렇게 무시당하도록 아무생각없이 그냥 만들어 버렸다. 쪽팔린다.

같은 기능을 하는데 아규먼트의 종류나 수가 다를경우 어떻게 설계해야 하는가.. 이때는 두가지 방법이 가능할 것 같다. 메소드의 오버로딩이나 아니면 그냥 메소드를 분리하는거다. 난 후자를 택했다. 이유는 어차피 때문이다.. 어차피 스태틱 메소드인걸.. 그런생각에

나는 항상 어떤 한계를 가지고 뭔가를 생각하는 것 같다 as-is를 분석하면 그 sa-is를 마치 지켜야 하는 성역인것 처럼 생각하게 된다. as-is에서 잘못된 것을 바로 잡는것이 to-be의 목적중 하나일텐데.. 잘못된것은 과감하게 잘라버리자. as-is의 한계속에서 나아가 기술적인 한계속에서 생각하지 말자

+ Recent posts