Spring Boot에서 테스트를 - 1

Spring Boot에서 테스트를 - 1

Spring Boot는 애플리케이션을 테스트하기 위한 많은 유틸 어노테이션을 제공합니다. Spring boot에서 테스트 모듈dms spring-boot-test와 spring-boot-test-autoconfigure가 존재합니다. 대부분의 경우는 spring-boot-starter-test만으로도 충분합니다. spring-boot-starter-test는 spring boot의 테스트에 사용되는 Starter 패키지입니다. spring-boot-starter-test는 JUnit는 물론이고, AssertJ, Hamcrest를 포함한 여러 유용한 라이브러리를 포함하고 있습니다.

포함되어있는 주요 라이브러리들

기존 Spring framework에서 사용하던 spring-test 이외에도 여러 유용한 라이브러리를 포함하고 있습니다. 가지고 있는 라이브러리에는 Mocktio도 포함되어있는데 기본적으로 Mocktio 1.x 버전을 사용합니다. 하지만 원한다면 2.x 버전를 사용하는 것도 가능합니다.

  • JUnit
  • Spring Test & Spring Boot Test
  • AssertJ
  • Hamcrest
  • Mockito
  • JSONassert
  • JsonPath

@SpringBootTest 어노테이션

spring-boot-test는 @SpringBootTest라는 어노테이션을 제공합니다. 이 어노테이션을 사용하면 테스트에 사용할 ApplicationContext를 쉽게 생성하고 조작할 수 있습니다. 기존 spring-test에서 사용하던 @ContextConfiguration의 발전된 기능이라고 할 수 있습니다. @SpringBootTest는 매우 다양한 기능을 제공합니다. 전체 빈 중 특정 빈을 선택하여 생성한다던지, 특정 빈을 Mock으로 대체한다던지, 테스트에 사용할 프로퍼티 파일을 선택하거나 특정 속성만 추가한다던지, 특정 Configuration을 선택하여 설정할 수도 있습니다. 또한, 주요 기능으로 테스트 웹 환경을 자동으로 설정해주는 기능이 있습니다. 앞에서 언급한 다양한 기능들을 사용하기 위해서 첫 번째로 가장 중요한 것은 @SpringBootTest 기능은 반드시 @RunWith(SpringRunner.class)와 함께 사용해야 된다는 것입니다.

Bean

@SpringBootTest 어노테이션을 사용하면 테스트에 사용할 빈을 아주 손쉽게 생성할 수 있습니다. @SpringBootTest 어노테이션은 classes라는 속성을 제공합니다. 해당 속성을 통해서 빈을 생성할 클래스들을 지정할 수 있습니다. classes 속성에 @Configuration 어노테이션을 사용하는 클래스가 있다면 내부에서 @Bean 어노테이션을 통해서 생성되는 빈도 모두 등록이 됩니다. 만일 classes 속성을 통해서 클래스를 지정하지 않으면 애플리케이션 상에 정의된 모든 빈을 생성합니다.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ArticleServiceImpl.class, CommonConfig.class})
public class SomeClassTest {
    // Service로서 등록된 빈
    @Autowired
    private ArticleServiceImpl articleServiceImpl;
    // CommonConfig에서 생성되는 빈
    @Autowired
    private RestTemplate restTemplate;
}

TestConfiguration

기존에 정의했던 Configuration을 커스터마이징하고 싶은 경우 TestConfiguration 기능을 사용할 수 있습니다. TestConfiguration은 ComponentScan 과정에서 생성될 것이며 해당 자신이 속한 테스트가 실행될때 정의된 빈을 생성하여 등록할 것입니다.

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestConfigArticleServiceImplTest {
    @MockBean
    private ArticleDao articleDao;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ArticleServiceImpl articleServiceImpl;

    @Test
    public void test() {
        String good = restTemplate.getForObject("test", String.class);
        assertThat(good).isEqualTo("Good");
    }

    @TestConfiguration
    public static class TestConfig {
        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate() {
                @Override
                public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
                    System.out.println("Good");
                    if (responseType == String.class) {
                        return (T) "Good";
                    } else {
                        throw new IllegalArgumentException();
                    }
                }
            };
        }
    }
}

ComponentScan을 통해서 감지되기 때문에 만일 @SpringBootTest의 classes 속성을 이용하여 특정 클래스만을 지정했을 경우에는 TestConfiguation은 감지되지 않습니다. 그런 경우 classes 속성에 직접 TestConfiguration을 추가해주어야 합니다. 하지만 더 좋은 방법은 @Import 어노테이션을 사용하는 것입니다. @Import 어노테이션을 통해서 직접 사용할 TestConfiguration을 명시할 수 있으며 특정 테스트 클래스의 내부 클래스가 아닌 별도의 클래스로 분리하여 여러 테스트에서 공유할 수도 있습니다.

@TestConfiguration
public class TestConfig {
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate() {
            @Override
            public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {
                System.out.println("Good");
                if (responseType == String.class) {
                    return (T) "Good";
                } else {
                    throw new IllegalArgumentException();
                }
            }
        };
    }
}
@RunWith(SpringRunner.class)
@SpringBootTest(classes = ArticleServiceImpl.class)
@Import(TestConfig.class)
public class TestConfigArticleServiceImplTest {
    @MockBean
    private ArticleDao articleDao;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ArticleServiceImpl articleServiceImpl;

    @Test
    public void test() {
        String good = restTemplate.getForObject("test", String.class);
        assertThat(good).isEqualTo("Good");
    }
}

MockBean and SpyBean

spring-boot-test 패키지는 Mockito를 포함하고 있기 때문에 기존에 사용하던 방식대로 Mock 객체를 생성해서 테스트하는 방법도 있지만, spring-boot-test에서는 새로운 방법도 제공하고 있습니다. @MockBean 어노테이션을 사용해서 이름 그대로 Mock 객체를 빈으로써 등록할 수 있습니다. 그렇기 때문에 만일 @MockBean으로 선언된 빈을 주입받는다면(@Autowired 같은 어노테이션 등을 통해서) Spring의 ApplicationContext는 Mock 객체를 주입해줍니다. 새롭게 @MockBean을 선언하면 Mock 객체를 빈으로써 등록을 하지만, 만일 @MockBean으로 선언한 객체와 같은 이름과 타입으로 이미 빈으로 등록되어있다면 해당 빈은 선언한 Mock 빈으로 대체됩니다.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ArticleServiceImpl.class)
public class ArticleServiceImplTest {
    @MockBean
    private RestTemplate restTemplate;
    @MockBean
    private ArticleDao articleDao;
    @Autowired
    private ArticleServiceImpl articleServiceImpl;

    @Test
    public void testFindFromDB() {
        List<Article> expected = Arrays.asList(
                new Article(0, "author1", "title1", "content1", Timestamp.valueOf(LocalDateTime.now())),
                new Article(1, "author2", "title2", "content2", Timestamp.valueOf(LocalDateTime.now())));

        given(articleDao.findAll()).willReturn(expected);

        List<Article> articles = articleServiceImpl.findFromDB();
        assertThat(articles).isEqualTo(expected);
    }
}

Properties

Spring Boot는 기본적으로 클래스 경로상의 application.properties(또는 application.yml)를 통해 애플리케이션 설정을 수행합니다. 하지만 테스트 중에는 설정이 기존과 달라질 필요가 있는 경우가 많은데 이때를 위한 기능을 SpringBootTest에서 제공하고 있습니다. SpringBootTest는 properties라는 속성이 존재합니다. 이 속성을 이용해서 별도의 테스트를 위한 application.properties(또는 application.yml)을 지정할 수 있습니다.

@RunWith(SpringBoot.class)
@SpringBootTest(properties = "classpath:application-test.yml")
public class SomeTest {
    ...
}

Web Environment test

앞에서 언급했듯이 @SpringBootTest 어노테이션을 사용하면 손쉽게 웹 테스트 환경을 구성할 수 있습니다. @SpringBootTest의 webEnvironment 파라미터를 이용해서 손쉽게 웹 테스트 환경을 선택할 수 있습니다. 제공하는 설정 값은 아래와 같습니다.

  • MOCK
    • WebApplicationContext를 로드하며 내장된 서블릿 컨테이너가 아닌 Mock 서블릿을 제공합니다. @AutoConfigureMockMvc 어노테이션을 함께 사용하면 별다른 설정없이 간편하게 MockMvc를 사용한 테스트를 진행할 수 있습니다.
  • RANDOM_PORT
    • EmbeddedWebApplicationContext를 로드하며 실제 서블릿 환경을 구성합니다. 생성된 서블릿 컨테이너는 임의의 포트는 listen합니다.
  • DEFINED_PORT
    • RAMDOM_PORT와 동일하게 실제 서블릿 환경을 구성하지만, 포트는 애플리케이션 프로퍼티에서 지정한 포트를 listen합니다(application.properties 또는 application.yml에서 지정한 포트)
  • NONE
    • 일반적인 ApplicationContext를 로드하며 아무런 서블릿 환경을 구성하지 않습니다.

TestRestTemplate

@SpringBootTestTestRestTemplate을 사용한다면 편리하게 웹 통합 테스트를 할 수 있다. TestRestTemplate은 이름에서 알 수 있듯이 RestTemplate의 테스트를 위한 버전입니다. @SpringBootTest에서 Web Environment 설정을 하였다면 TestRestTemplate은 그에 맞춰서 자동으로 설정되어 빈이 생성됩니다.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RestApiTest {
    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void test() {
        ResponseEntity<Article> response = restTemplate.getForEntity("/api/articles/1", Article.class);
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
        assertThat(response.getBody()).isNotNull();
        ...
    }
}

기존에 컨트롤러를 테스트하는데 많이 사용되던 MockMvc와 어떤 차이가 있는지 궁금할 것입니다. 가장 큰 차이점이라면 Servlet Container를 사용하느냐 안하느냐의 차이입니다. MockMvc는 Servlet Container를 생성하지 않습니다. 반면, @SpringBootTestTestRestTemplate은 Servlet Container를 사용합니다. 그래서 마치 실제 서버가 동작하는 것처럼(물론 몇몇 빈은 Mock 객체로 대체될 수는 있습니다) 테스트를 수행할 수 있습니다. 또한, 테스트를 하는 관점도 서로 다릅니다. MockMvc는 서버 입장에서 구현한 API를 통해 비지니스 로직이 문제없이 수행되는지 테스트를 할 수 있다면, TestRestTemplate은 클라이언트 입장에서 RestTemplate을 사용하듯이 테스트를 수행할 수 있습니다.

트랜젝션

이때 주의해야할 점이 있는데 바로 @Transactional 어노테이션입니다. spring-boot-test는 그저 spring-test를 확장한 것이기 때문에 @Test 어노테이션과 함께 @Transactional 어노테이션을 함께 사용하면 테스트가 끝날때 rollback됩니다. 하지만 RANDOM_PORT나 DEFINED_PORT로 테스트를 설정하면 실제 테스트 서버는 별도의 스레드에서 수행되기 때문에 rollback이 이루어지지 않습니다.

ApplicationContext 캐시

참고로 @SpringBootTest 기능으로 인해서 생성된 ApplicationContext를 캐시됩니다. 만약에 @SpringBootTest의 설정이 동일하다면 동일한 ApplicationContext를 사용하게 됩니다.

다음 문서에서는

spring-boot-test는 테스트를 효율적이게 할 수 있는 많은 도구들을 제공합니다. 지금까지는 테스트를 하기위한 기초적인 부분들을 정리하였습니다. 다음 문서에서는 아래의 나열한 기능들에 대해서 살펴보겠습니다.

  • @WebMvcTest
  • @DataJpaTest
  • @JdbcTest
  • @DataMongoTest
  • @JsonTest
  • @RestClientTest

Vuejs with Bootstrap

Vue.js with Bootstrap

Vue를 공부하면서 Vue에서 Bootstrap을 편하게 사용하기 위해서 알아봤던 방법에 대해서 공유하고자 한다.

Vue.js

최근 FE 프레임워크 진영은 마치 삼국지를 연상하게 한다. React, Angular2 그리고 Vue. 비록 Vue가 아직 React와 Angular 시리즈에 비하면 역사가 오래되지 않았지만, 점점 자신의 영역을 넓혀가는 중이다. 한국에서도 점점 많은 개발자들이 관심을 보이고 있으며 한국 포럼도 존재하고 기술 문서도 한글 번역이 이루어 졌다. 또한, Vue는 앞에서 언급한 두 개의 프레임워크보다 더 사용하기 쉬우며, 성능도 꿀리지 않는다. 그리고 그 특유의 유연함을 통해서 규모가 작은 앱부터 큰 앱까지 아주 편하게 사용할 수 있다.

도구없는 환경에서 Bootstrap을 함께 사용하기

사실 도구(webpack, Browserify 등)가 없는 환경은 너무 간단해서 더이상 설명할 필요도 없어보인다. 하지만 간단히 경량 앱에서 Vue를 사용하는 모습을 소개하기 위해서 간단하게 예제를 보고 가겠다. 자세한 사항은 여기보다 물론 아무런 도구를 사용하지 않기 때문에 ES6이상의 문법은 전혀 사용되지 않는다.

...
<head>
  ...
  <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css">
</head>
<body>
  <div id="container">
    <form class="form-inline">
      <div class="form-group">
        <input type="text" class="form-control" v-model="message">
        <p></p>
        <button type="button" class="btn btn-default" @click="alertMessage">Alert</button>
      </div>
    </form>
  <div>

  <script src="jquery/js/jquery.min.js"></script>
  <script src="bootstrap/js/bootstrap.min.js"></script>
  <script src="vue/js/vue.min.js"></script>
  <script src="main.js"></script>
</body>
...
  new Vue({
    el: '#container',
    data: {
      message: ''
    },
    methods: {
      alertMessage: function () {
        alert(this.message);
      }
    }
  });

Vue 컴포넌트와 함께 Bootstrap 적용하기

Vue를 컴포넌트(Single File Component)로 나누어 본격적으로 Vue를 사용하기 시작하면 Bootstrap을 사용하는데 고려할 사항이 늘어난다. 앱이 컴포넌트 단위로 분리되며 모듈화를 위해서 각 컴포넌트는 별도의 파일로 분리될 것이다. 또한, 각 컴포넌트는 자신의 JavaScript 로직 및 디자인을 위한 CSS가 적용된다. 이러한 컴포넌트들(vue파일들)을 빌드하여 브라우저에서 돌아가는 JavaScript로 바꾸기 위해서 Webpack이라는 도구를 사용한다. 그리고 앱의 의존성 관리를 위해서 NPM을 사용할 것이고 배포 환경별로 프로파일을 분리할 것이다. 개발 환경에서는 사용하는 라이브러리들의 일반 버전을 사용하고 서비스 환경에서는 라이브러리들의 min 버전을 사용하고 난독화 및 압축을 해야할테니 말이다. 여기서는 단순히 많이 사용되는 Bootstrap을 Vue로 개발하는 앱에 적용하는 방법에 대해서 몇가지를 정리할 것이다. 개발 환경은 vue-cli를 통해 생성된 프로젝트를 기준으로 한다.

static resource로 적용하기

위 ‘도구없는 환경에서 Bootstrap을 함께 사용하기’에서 설명한 방법이다. Vue Project의 index.html에 직접 Bootstrap의 CSS와 JavaScript를 추가해주는 방법이다. 처음 웹을 공부할때부터 사용하던 방법이니 매우 간단해보이지만 이 방법으로는 Webpack이 주는 해택을 이용할 수 없다.

앱 전역으로 Bootstrap을 적용하기

일단 NPM을 통해서 Boostrap을 설치한다.

$ npm install --save jquery bootsrap

위 명령을 실행하고 package.json을 확인하면 의존성에 JQuery와 Bootstrap이 추가된 것을 볼 수 있다. 이제 실제 Vue 로직내에서 JQuery의 기능을 사용하기 위해서 $ 키워드를 전역으로 적용을 할 것이다. 그러기 위해서는 expose-loader가 필요하다(loader가 뭔지는 webpack 문서 참고). npm을 통해서 expose-loader도 설치한다.

$ npm install --save-dev expose-loader

expose-loader를 사용하면 자신이 원하는 키워드로 라이브러리를 바인딩할 수 있다. 그러기 위해서 main.js에 아래의 코드를 추가한다.

import 'expose-loader?$!expose-loader?jQuery!jquery'

expose-loader의 expose-loader?libraryName!./file.js 같은 형식으로 정의하여 사요할 수 있다. ‘?’ 다음에 작성하는 libraryName은 바인딩할 키워드이며 ‘!’ 다음에 실제 라이브러리 경로를 작성한다. ‘!’ 다음에 expose-loader를 반복하여 작성해서 동시에 여러개를 바인딩할 수 도 있다. 위 코드에 경우 JQuery를 $와 jQuery에 바인딩하는 것이다. 이를 통해 모든 컴포넌트가 JQuery를 사용할 수 있다. 이제 Bootstrap을 추가할 차례이다.

Bootstrap을 추가하는 방법은 매우 간단하다. 앞에서 NPM을 통해서 Bootstrap을 설치하였기 때문에 Bootstrap을 import해주기만 하면 된다.

import 'bootstrap'

문제는 CSS이다. vue-cli를 통해서 프로젝트를 생성하였다면 css-loader가 이미 설정되어 있을텐데 추가적으로 style-loader를 추가해주어야 한다.

$ npm install --save-dev style-loader

Before

// in webpack.config.js
...
{
  test: /\.css$/,
  loader: ['css-loader']
}
...

After

// in webpack.config.js
...
{
  test: /\.css$/,
  loader: ['style-loader', 'css-loader']
}
...

그리고 Bootstrap의 몇몇 리소스들을 로드하기 위해서 추가적으로 file-loader에 파일 확장자를 추가해야 한다.

...
{
  test: /\.(png|jpg|gif|svg|eot|woff|woff2|ttf)$/,
  loader: 'file-loader',
  options: {
    name: '[name].[ext]?[hash]'
  }
}
...

그후 Bootstrap CSS를 import해주어야 한다.

// in main.js
import 'bootstrap/dist/css/bootstrap.css'

이로서 Bootstrap을 사용하기 위한 준비가 완료되었다. 이제 어떤 컴포넌트든 Bootstrap을 사용할 수 있다.

// in some vue component
<template>
  <div>
    <h1 class="title">Hello!!!</h1>
    <input class="form-control" v-model="message">
    <button type="button" class="btn btn-default" @click="show">
    <some-component :value="message"></some-component>
  </div>
</template>

<script>
  import someComponent from './some/component.vue'

  export default {
    data () {
      message: ''
    },
    methods: {
      show () {
        alert(this.message)
      }
    },
    components: {
      someComponent
    }
  }
</script>

<style scoped>
  .title {
    font-size: 20pt;
    font-weight: bolder;
    border-bottom: 1px solid #999;
  }
</style>

Boostrap-vue 플러그인 이용하기

Bootstrap을 사용하는 또다른 방법은 Bootstrap-vue 플러그인을 사용하는 것이다. Bootstrap-vue는 Vue에서 사용할 수 있도록 Bootstrap을 컴포넌트로 만든 플러그인이다(참고로 Bootstrap-vue는 Bootstrap4를 바탕으로 한다).

$ npm install --save bootstrap-vue
// in main.js
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue';

Vue.use(BootstrapVue);

// using style-loader
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

위 코드처럼 필요한 설치과정을 마무리하면 Vue 컴포넌트 어디서든 Bootsrap-vue에서 만든 컴포넌트를 사용할 수 있다. 사용 예제는 아래와 같다.

<template>
  <div>
    <b-card header="Todo List" :no-block="tasksExist">
      <b-list-group v-if="tasksExist">
        <b-list-group-item v-for="task in tasks" :key="task.id" @click.native="moveToDoingList">
          <task :data="task"></task>
        </b-list-group-item>
      </b-list-group>
      <h5 v-else>
        해야할 일이 없습니다.
      </h5>
    </b-card>
  </div>
</template>

<script>
import task from './Task.vue';

export default {
  props: ['tasks'],
  components: {
    task
  },
  computed: {
    tasksExist () {
      return this.tasks && this.tasks.length > 0
    }
  },
  methods: {
    moveToDoingList () {
      alert('아직 구현이 안됬습니다!!')
    }
  }
};
</script>

<style scoped>
  .no-padding {
    padding: 0;
    margin: 0;
  }
</style>

DDD 이야기 part1

Domain Driven Design

composition_8

우리는 프로그래밍을 통해 무엇을 이루려고 하는가?

현실의 본질(domain, 이하 domain 으로 통일)이 가지고 있는 문제나 요구사항을(needs) 해결하기 위해 소프트웨어를 설계한다. domain의 복잡성이 증가할 수록 소프트웨어의 복잡성또한 증가하게 된다. domain을 제대로 이해하고 설계를 해야 좋은 소프트웨어가 만들어질 가능 성이 높아진다.

그럼 domain을 어떻게 소프트웨어로 바꾸는가

본질을 추상화 해서 모델을 만들고 만들어진 모델을 바탕으로 소프트웨어를 만든다.

잘 이 매우 중요하다... (후술)

모델

domain을 분석해서 모델을 만드는 일은 사실 늘 해오던 일이다. 다만 여기서 주의해야 할 점은 분석을 통해 얻어진 ‘분석 모델’과 실제 구현에 쓰일 ‘설계 모델’이 달라지면 안된다는 점이다. ‘분석 모델’과 ‘설계 모델’이 나뉘게 되면 domain에 요구사항이 늘어나거나, 변경이 생기거나, 시간의 경과로 인해 두 모델간의 결합성이 약해지게 된다는 것이다. 모델은 domain을 표현하기 위한 방법이지 구현을 편하게 하는 방법이 아니라는 점이다. 당장 구현이 복잡해 보일지라도 장기적으로 봤을 때 모델 속에 내재된 domain을 잃지 않는 것이 소프트웨어의 생명을 유지시켜줄 것이다.

‘잘’ 추상화 하는 방법

어떻게 하면 domain 을 ‘잘’ 추상화 할 수 있을까? DDD 에서는 분석 및 설계시에 domain을 제대로 알고 있는 전문가가 참여하고, 프로젝트 구성원 모두가 모델링 작업에 참여하기를 권장합니다.

그런건 우리에게 있을 수 없어

도메인 전문가와 구성원들의 공통된 지식을 가지고 공통된 언어로 군더더기 없는 공통의 모델을 상상하되, 구현이 가능해야하는 모델이 산출되야 잘 만들어진 모델이라 할 수 있다.

그런건 우리에게 있을 수 없어

현실로 다시 돌아와서, 현실에서의 우리(상당히 많은 개발자들)는 도메인 전문가를 매 상황마다 모실 수가 없다. 혹은 내가 ‘First Penguin’이라면? 내가 도메인 전문가가 되어야 한다. 적어도 전문가가 되려고 노력해야 한다. 분석을 토대로 모델링을 할 때, 모델링 하기 편한 모델링이 아니라 당면한 domain의 본질을 담기 위한 모델링을 해야한다.

DDD 를 하기 위한 준비물 2가지

Ubiquitous Language

domain 전문가와 원활하고 지속적인 대화를 해야한다. 개발자들은 domain 전문가의 용어를 이해하고, 이해한 바를 모델에 풀어낸다. 도메인에 대한 지적 탐구를 지속해야 하고 종래에는 개발자도 domain 전문가가 하는 모든 용어를 알아들을 수 있어야 한다. 그들과 같은 언어와 배경지식을 공유해야 모델링 하는데 있어서 domain을 잃지 않을 수 있다.

Model Design Driven

그토록 중요한 모델이 생명력을 잃지 않고 지속적으로 관리되고, domain의 본질을 잃지 않게 유지하는 것이 Model Design Driven 이다. 모델이 생명력을 잃지 않기 위해 몇가지 패턴들이 쓰인다.

DDD 를 위한 패턴들

계층형 아키텍쳐

소프트웨어를 설계할 때, 항상 목적성을 지녀야 한다. ddd_layer 목적성에 맞게 필요한 모듈들을 모을 수 있어야 한다.

Entity

식별자가 있고 영속성이 필요한 Object를 entity라고 한다. 반드시 식별자가 있어야 하고, 식별자가 같으면 같은 Object 인 것이 보장되어야 한다.

Value Object

Entity와 비슷한데, 식별자가 없고, 영속성이 필요하지 않은 Object들.

Service

상태정보를 관리하지는 않지만, 행위 자체를 담당하는 것들을 Service 라고 칭한다.

Aggregation

외부에서 접근하는 방법은 하나지만, 하나의 단위로 간주되는 관련된 객체들의 집합이다. transaction, 데이터의 무결성 등을 처리하기에 용이하다.

Factory

복잡한 절차를 지닌 Entity 들의 생성을 Factory로 묶어서 생성을 관리한다.

Repository

객체를 저장하는 일을 담당하는 녀석이다.

간략하게 DDD 에 대해 정리해 봤고, 다음번는 DDD를 위한 패턴들 이야기와 애자일 이야기를 좀 더 하려고 한다.


글을 쓰기 위해 도움을 얻었던 글목록

python selenium 구현

이것은 파이선 테스트 입니다.

import selenium for webdriver
print('hello world')

Hello SQL

hello

select * from dual;