달력

5

« 2024/5 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
2008. 3. 17. 22:13

Spring, DDD and AspectJ - An Example 프레임워크2008. 3. 17. 22:13

아래와 같은 코드를 예로 들어보자.

public class Customer {

private Long accountId;

public Long getAccountId() {
return accountId; }
}

public void setAccountId(Long accountId) {
this.accountId = accountId;
}
...
}

public class CustomerServiceImpl {
public Double getNetWorth(Customer customer) {

Long accountId = customer.getAccountId();

Account account = accountRepository.getAccountById(accountId);

return account.getValue();
}

}

위의 예는 전형적인 Anemic Domain Model이다(행위는 서비스 레이어에만 있고, 도메인 객체는 단순히 데이터 컨테이너 역할만 한다).

이를 해결하고자 할 때 부딪히는 가장 큰 문제는 Cutomer객체가 어떻게 Account객체를 얻느냐는 것이다. 이 문제에 대한 해결책은 아래와 같은 3가지가 있을 수 있다.

  1. CustomerServiceImpl이 Account를 얻어서 Customer객체에 전달한다. 이 경우 CustomerServiceImpl은 여전히 2개의 비즈니스 로직에 참여하게 되서 위 코드와 별반 다를바가 없게 된다.
  2. Cutomer객체가 service locator를 통해 능동적으로(actively) AccountRepository를 얻는다. 이 방법은 Customer 객체가 ArticleRepository와 service locator에 종속성(dependency)을 갖게 해서 transparent한 해결책이라 할 수 없다.
  3. DI를 통해 ArticleRepository를 Customer 객체에 주입한다.

위 방법 중에 3안이 가장 이상적인 방안으로 보인다.

3안은 아래와 같이 구현될 수 있다.

@Configurable("smartCustomer")
public class SmartCustomer extends Customer {
private AccountRepository accountRepository;

public AccountRepository getAccountRepository() {
return accountRepository;
}

public void setAccountRepository(AccountRepository accountRepository) {
this.accountRepository = accountRepository;
}

public Double getNetWorth() {
Account account = accountRepository.getAccountById(getAccountId());
return account.getValue();
}
}

@Configurable은 해당 클래스가 새로운 인스턴스가 생성될 때 Spring에 의해 DI되어야 한다는 표식이다. @Configurable이 붙은 클래스를 스프링에 등록해 주면 스프링은 그 객체가 생상될 때 그 객체가 필요로 하는 bean들을 주입해준다. 이를 위한 설정 파일은 아래와 같다.

<bean id="smartCustomer" abstract="true" class="org.springdallasug.aspectj.domain.SmartCustomer" scope="prototype" >
<property name="accountRepository" ref="accountRepository" />
</bean>

그리고 LTW로 DI가 발생하도록 하기 위해 JVM 실행 옵션에 아래 라인을 추가한다.

-javaagent:PATH_TO_JAR/aspectjweaver-1.5.0.jar

Pros and Cons
나도 같은 생각이지만 이 글의 필자도 도메인 객체에 DI를 적용하는 것이 맞는지 의문이라고 한다. 예제와 같은 스타일로 구현하게 되면 도메인 객체는 자신의 역할 수행(net worth 계산)을 위한 완벽한 로직을 갖게 되는 장점을 갖는다. 하지만 도메인 객체가 리파지토리를 사용함으로써 이상한 설계가 된다.
보다 풍부한(richer) 도메인 모델이 되지만 복잡해질 수도 있는 보다 많은 종속성이 도메인 모델에 생긴다.

구현 후기
- service 메소드가 인자로 Customer를 받으면 동작하지 않는다. SmartCustomer를 받아야만 제대로 동작한다.

참고자료

:
Posted by codetemplate