Eternity's Chit-Chat

aeternum.egloos.com



Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 7부 Domain-Driven Design

Spring 컨테이너 외부에서 생성되는 객체에 대해 의존성 주입을 제공하는 가장 효과적인 방법은 AOP(Aspect-Oriented Programming)를 적용하는 것이다. AOP란 시스템 내의 관심사를 분리하는 프로그래밍 기법이다. AOP를 사용하면 시스템의 핵심 관심사(Core Concerns)와 횡단 관심사(Cross-Cutting Concerns)의 분리를 통해 결합도가 낮고 재사용이 가능한 시스템을 개발할 수 있다.

Spring 2.x부터 기존의 프록시 기반 메커니즘에 AspectJ를 통합하여 더 강력하고 유연한 AOP 기능을 지원하게 되었다. 그 대표적인 것이 Spring 컨테이너 외부에서 생성되는 도메인 객체에 Spring 컨테이너에서 관리하는 빈을 의존 삽입할 수 있도록 해주는 기능이다. 이것은 AspectJ 5부터 지원되는 LTW(Load-Time Weaving) 기능을 사용하는 것으로 클래스 로더가 클래스를 로드 할 때 바이트 코드를 수정하여 Spring 빈을 삽입하는 것이다. 따라서 Order에서 new 연산자를 사용하여 OrderLineItem을 생성하더라도 CollectionProductRepository를 의존 삽입하는 것이 가능해 진다.

AspectJ
LTW를 사용하기 위해서는 “META-INF/aop.xml” 파일을 설정해야 한다.


aop.xml
<!DOCTYPE aspectj PUBLIC
  "-//AspectJ//DTD//EN"    "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">

<!--
 
CGLIB으로 생성된 오브젝트에 대해서는 weaving 제거한다.
   이 옵션은 어플리케이션 스타트 속도를 향상시킨다.
-->
<aspectj>
  <weaver>
    <include within="org.eternity..*"/>       
    <exclude within="org.eternity..*CGLIB*"/>
  </weaver>
  <aspects>
    <include within="org.springframework.beans.factory.aspectj
                     
.AnnotationBeanConfigurerAspect"/>
  </aspects>
</aspectj>


<include>
엘리먼트를 사용하여 org/eternity 클래스 패스 하위에 포함된 클래스들에 대해서만 애스펙트를 적용하도록 설정한다. CGLIB에 의해 생성된 프록시 객체들은 애스펙트를 적용할 필요가 없으므로 <exclude> 엘리먼트를 사용하여 제외시킨다.

이제 Spring에게 OrderLineItem의 클래스를 로드할 때 어떤 방법으로 ProductRepository 인스턴스를 의존 삽입해야 하는지를 알려줘야 한다. Spring 빈 컨텍스트 설정 파일에 OrderLineItem ProductRepository의 연결 정보를 추가하자


order-beanContext.xml
<context:load-time-weaver/>
<context:spring-configured/>
<bean id="orderLineItem" 
    class="org.eternity.customer.OrderLineItem" abstract="true">
    <property name="productRepository" ref="productRepository"/>
</bean>


Spring에게 OrderLineItem의 클래스가 로딩될 때 productRepository 프로퍼티에 id“productRepository”로 선언된 빈을 의존 삽입하도록 설정했다. 실제로 빈을 생성할 필요는 없으므로 abstract 속성을true설정했다.

<context:spring-configured>
 
는 뒤에서 살펴 볼 @Configurable 어노테이션이 선언된 클래스들을 Spring 컨테이너가 관리하도록 설정한다
. <context:load-time-weaver/>LTW를 사용하도록 선언한다.


OrderLineItem.java
package org.eternity.customer;

import
org.springframework.beans.factory.annotation.Configurable;

@Configurable
(value="orderLineItem",preConstruction=true)
public class OrderLineItem {
  private ProductRepository productRepository;

  public OrderLineItem() {
 
}

  public OrderLineItem(String productName, int quantity) {
    this.product = productRepository.find(productName);
   
this.quantity = quantity;
  }

  public void setProductRepository(ProductRepository productRepository) {
   
this.productRepository = productRepository;
  }


@Configurable 어노테이션의 value에는 Spring 빈 컨텍스트에 정의한 OrderLineItem 빈의 id를 정의한다. preConstruction의 값을 true로 한 이유는 이 값을 true로 설정하지 않으면 기본적으로 생성자 호출이 끝난 후 의존성이 주입되기 때문이다. 따라서 위와 같이 생성자 내부에서 주입될 대상 객체를 호출하는 경우 NullPointerException이 발생하게 된다. 이를 방지하기 위해서는 생성자가 호출되기 전에 의존성이 주입되도록 preConstruction의 값을 true로 설정해 주어야 한다.

이제 테스트를 실행하기 위해 JVM–javaagent 옵션의 값으로 spring-agent.jar의 전체 경로를 주도록 하자. 아래의 fullpath 부분은 각자의 개발 환경에 맞게 경로를 설정해 주면 된다

-javaagent:/fullpath/spring-agent.jar

이제 테스트를 실행해 보자. 녹색 막대다! 드디어 Spring 컨테이너를 사용하여 주문 시스템의 전체적인 결합도를 낮추는데 성공했다. 도메인 클래스들이 REPOSITORY의 인터페이스에만 의존할 뿐 실제적인 구현 클래스에 의존하지 않게 되었다. 힘든 리팩토링이었다. 이제는 메모리 컬렉션을 처리하는 REPOSITORY의 내부 구현을 데이터베이스에 접근하는 REPOSITORY로 대체하더라도 다른 클래스에 영향을 미치지 않을 것이다. 도메인 클래스가 REPOSITORY의 인터페이스에만 의존하기 때문에 Mock 객체를 사용하여 데이터베이스 없이도 테스트하는 것이 가능해졌다. 회귀 테스트도 성공했고 모든 것이 만족스럽다. 아차, 다운된 주문 시스템 때문에 고객들이 난리였었지. 실실 웃고 있을 때가 아니다. 실업자가 되기 전에 어서 주문 시스템에 영속성 메커니즘을 추가하도록 하자.


핑백

  • Domain-Driven Design | Jongmin Kim&#039;s Blog 2014-09-02 01:18:21 #

    ... riven Design의 적용-4.ORM과 투명한 영속성 1부 2009/01/18 Domain-Driven Design의 적용-3.Dependency Injection과 Aspect-Oriented Programming 7부 [2] 2009/01/02 Domain-Driven Design의 적용-3. ... more

덧글

  • 토비 2009/06/10 13:19 # 삭제

    지금까지 봤던 DDD의 적용에 관한 가장 탁월한 글인 것 같네요. 책으로 엮어서 내시면 좋겠어요.
    좋은 내용을 공개해 주셔서 고맙습니다.
  • 이터너티 2009/06/10 14:10 #

    토비님 안녕하세요. 저 역시 토비님의 글 잘 읽고 있습니다. 이런 곳에서 처음으로 인사 드리게 될 줄 몰랐습니다. ^^ 많이 부족한 글인데 칭찬해 주셔서 감사합니다.
  • 정주행중 2016/10/14 12:29 # 삭제

    이 좋은 글을 왜 이제야 보게 되었는지, 제가 안타깝네요. DDD 적용 파트 읽으면서 머리가 맑아지는 느낌입니다.
※ 로그인 사용자만 덧글을 남길 수 있습니다.