ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 도메인 주도 설계의 적용 - 1. 값 객체와 참조 객체 4부
    [옛날 글들] 도메인 주도 설계 2024. 4. 10. 18:10
    728x90

    이전글 : 도메인 주도 설계의 적용 - 1. 값 객체와 참조 객체 3부

    이 글은 제가 2008년 6월부터 10월까지 5개월간 마이크로소프트웨어에 연재했던 "도메인 주도 설계의 적용"이라는 원고의 원글입니다.  잡지에 맞추어 편집을 하는 과정에서 지면 상의 제약으로 인해 수정되거나 삭제된 부분이 있어 제 블로그에 원글을 올립니다. 도메인 주도 설계(Domain-Driven Design)에 관심 있는 분들에게 도움이 되었으면 합니다. 

     

    지금까지 살펴본 방법에서는 Customer 클래스 자체에 엔트리 포인트의 컬렉션을 관리하는 인터페이스를 추가함으로써 시스템 내에 엔트리 포인트의 인스턴스가 하나만 유지되도록 만들었다. 또 다른 방법으로는 Customer 클래스와는 분리된 별도의 클래스에 Customer 클래스의 컬렉션 관리 인터페이스를 할당하는 방법이 있다.

     

    우선 테스트 케이스를 작성하자.

    public class CustomerTest extends TestCase {
      public void setUp() {
        Registrar.init();
      }
    
      public void testCustomerIdentical() {
        CustomerRepository customerRepository = new CustomerRepository();    
        Customer customer = new Customer("CUST-01", "홍길동", "경기도 안양시");
        customerRepository.save(customer);
        Customer anotherCustomer = customerRepository.find("CUST-01");
    
        assertSame(customer, anotherCustomer);
      }
    }

     

    Customer 클래스를 통해 컬렉션 관리를 수행하던 이전의 예제와 달리 이번에는 CustomerRepository라는 별도의 리포지토리 객체를 사용해서 객체 컬렉션을 관리하고 있다.

     

    별도의 객체로 검색 메커니즘을 분리했으므로 EntryPoint는 단순히 검색 키를 반환하는 메서드만을 제공하면 된다. 다음은 persist() 메서드를 제거한 EntryPoint 클래스 코드를 나타낸 것이다.

    package org.eternity.common;
    
    public class EntryPoint {
      private final String identity;
    
      public EntryPoint(String identity) {
        this.identity = identity;
      }
    
      public String getIdentity() {
        return identity;
      }
    }

     

    Customer 클래스는 이제 더 이상 save() find() 메소드와 같은 컬렉션 관리 메서드를 제공하지 않아도 된다. 과감히 제거하자.

    package org.eternity.customer;
    
    import org.eternity.common.EntryPoint;
    
    public class Customer extends EntryPoint {
      private String customerNumber;
      private String name;
      private String address;
      private long mileage;    
    
      public Customer(String customerNumber, String name, String address) {
        super(customerNumber);
        this.customerNumber = customerNumber;
        this.name = name;
        this.address = address;
      }    
    
      public void purchase(long price) {
        mileage += price * 0.01;
      }
    
      public boolean isPossibleToPayWithMileage(long price) {
        return mileage > price;
      }
    
      public boolean payWithMileage(long price) {
        if (!isPossibleToPayWithMileage(price)) {
          return false;
        }
    
        mileage -= price;
    
        return true;
      }
    
      public long getMileage() {
        return mileage;
      }
    }

     

    Customer에 대한 컬렉션 관리 메커니즘을 제공하는 CustomerRepository Registrar를 사용하기 위한 세부 내용을 캡슐화한다.

    package org.eternity.customer;
    
    import org.eternity.common.Registrar;
    
    public class CustomerRepository {
      public void save(Customer customer) {
        Registrar.add(Customer.class, customer);
      }
    
      public Customer find(String identity) {
        return (Customer)Registrar.get(Customer.class, identity);
      }
    }

     

    테스트 케이스를 실행하자. 마침내 녹색 막대다! 예상한 대로 동일한 객체가 반환되었다. 한숨 돌렸다. 바야흐로 ENTRY POINT의 컬렉션을 관리할 수 있는 기본 인프라가 갖추어진 것이다. 이제 ENTRY POINT를 효율적으로 관리할 수 있게 되었다.

     

    도메인의 복잡성

    소프트웨어는 복잡하다. 0 1의 비트들을 밀어넣으면 모든 것이 동작할 것이라는 단순한 예상과 달리, 0 1을 조합할 수 있는 무수히 많은 방법이 존재하기 때문에 소프트웨어는 복잡하다. 인간의 언어에 비해 표현력에 한계를 지닌 컴퓨터의 언어를 사용하기 때문에 단순할 것이라는 예상과 달리, 복잡한 인간 세상을 담아내야 하기 때문에 소프트웨어는 복잡하다. 설상가상으로 소프트웨어를 만드는 것이 근본적으로 불완전한 인간이기 때문에 소프트웨어 개발 과정 역시 복잡하고 불완전할 수밖에 없다.

     

    따라서 사람들은 지수적인 소프트웨어의 본질적인 복잡성을 선형적으로 완화하기 위해 다양한 기법들을 적용해 왔다. 이 기법들의 우두머리가 추상화(abstraction). 불필요한 군더더기를 제거하고 현재의 문제 해결에 필요한 핵심 개념만을 끌어안음으로써 문제영역의 복잡성을 감소시키는 기법을 추상화라고 한다. 참조 객체 값 객체의 개념 역시 도메인 영역을 추상화시키는 한 방법이다.

     

    참조 객체 값 객체의 분리는 도메인 개념들의 추적성 및 식별성을 추상화하기 위한 분석 기법이다. 도메인 영역의 개념을 참조 객체 값 객체로 분리함으로써 도메인의 본질적인 특성에 초점을 맞추게 된다.

     

    고객은 유일한가? 고객을 유일하게 구분 짓는 특성은 무엇일까? 시스템은 고객의 주문 및 구매 기록을 추적하기 위해 어떤 처리를 해야 하는가? 금액은 유일할 필요가 없는가? 단순하게 값만 비교하면 되는가?

     

    도메인 개념에 대한 이런 질문들은 도메인에 대한 이해를 향상하고 소프트웨어의 전반적인 복잡성을 완화하는 유용한 분석 기법이다. 참조 객체를 식별함으로써 시스템의 핵심 개념들의 생명 주기에 초점을 맞출 수 있다. 값 객체를 식별함으로써 도메인의 일부지만 중요하지 않은 개념들을 걸러낼 수 있다.

     

    메모리 상에서 참조 객체 값 객체를 관리하는 것은 그리 어렵지 않다. 그러나 데이터베이스와 같은 영속성 메커니즘이 끼어드는 순간 세상은 급격하게 일그러진다. 그 위에 다중 사용자 지원을 위한 동시성 메커니즘이 얹어지기라도 하면 참조 객체 값 객체에 대한 근본적인 개념에 금이 가기 시작한다. 이제부터는 단순히 언어적인 측면에서 객체를 구별하던 참조 객체 값 객체의 개념을 넘어 인프라 스트럭쳐나 구현과 세부 구현과 관련된 문제도 함께 고려해야 한다.

     

    다음 아티클에서는 참조 객체 값 객체를 사용하여 간단한 도메인을 모델링하고 이번 아티클에서 간단하게 설명한 엔트리 포인트 애그리게이트, 리포지토리에 관해 좀 더 자세히 살펴보기로 한다. 

     

    1부 끝


    다음글 : 도메인 주도 설계의 적용 - 2. 애그리게이트와 리포지토리 1부

    728x90
Designed by Tistory.