-
DDD - Dependency Injection과 AOP 1부JAVA/DDD 2021. 1. 11. 19:29
- 애플리케이션의 모든 기억들은 종료되는 순간 깨끗하게 증발돼 버리고 만다.
- 애플리케이션은 상태를 끊임없이 기록하고, 기억을 재생하고, 현실을 재구성해야 한다.
- 유효 시간이 지나고 단기 기억 상실증으로 인해 모든 기억이 소멸되더라도 기록을 통해 어플리케이션의 기억을 되돌릴 수 있어야 한다.
- 애플리케이션이 기억을 재생할 수 있다고 해서 모든 기억을 동시에 복구하는 것은 소모적인 일이다.
- 살아오면서겪은 모든 일들을 일일이 다 기억하고 있을 수는 없다.
- 애플리케이션 역시 마찬가지다. 지금 처리하기 위해 필요한 최소한의 정보만 기억하고 있으면 된다.
- 시스템의 메모리는 한정된 자원이다.
- 지금 당장 필요하지도 않은 정보들을 유지하기 위해 값비싼 자원을 낭비할 필요는 없다.
- 잠시 잊어 버렸다가 필요할 때 기록을 들춰 보면 된다.
- 인간이 컴퓨터에게 준 최고의 선물은 영구적인 기억력이다.
- 그러나 영구적인 기억력이 하늘에서 뚝하고 떨어지는 것은 아니다.
- 애플리케이션을망각의 늪에서 구원하기 위해서는 지금까지 우리가 망각하고 있던 한 가지 요소가 필요하다.
- 바로 영속성(persistence)이 그것이다.
도메인 객체의
- 주문 도메인에서 ENTRY POINT에 대한 저장, 조회 등의 컬렉션 연산을 수행하기 위해 REPOSITORY를 사용했다.
- 주문 도메인을 분석한 후 Customer, Product, Order를 ENTRY POINT로 식별하고
- 각 ENTRY POINT의 생명 주기를 관리하기 위해 CustomerRepository, ProductRepository, OrderRepository를 도메인 모델에 추가했다.
- Order와 OrderLineItem의 생명 주기를 좀 더 자세히 살펴보자.
- Order와 OrderLineItem은 Customer의 newOrder() 메소드를 통해 생성된다.
- newOrder() 메소드는 Order의 CREATION METHOD인 order()를 호출하며, 이 메소드는 내부적으로 생성자를 호출하여 Order 클래스를 생성한 후 반환한다.
- OrderLineItem은 Order의 with() 메소드를 사용하여 생성되며, with() 메소드 역시 OrderLineItem의 생성자를 호출하여 인스턴스를 생성한다.
12345678910111213141516public class Order {public static Order order(String orderId, Customer customer) {return new Order(orderId, customer);}Order(String orderId, Customer customer) {super(orderId);this.customer = customer;}public Order with(String productName, int quantity)throws OrderLimitExceededException {return with(new OrderLineItem(productName, quantity));}}cs - Order와 OrderLineItem의 생성자가 호출되는 순간 사용자가 입력한 주문 정보를 저장하고 있는 주문 AGGREGATE가 생성된다.
- 그러나 객체를 생성하는 것으로 끝난다면 지금까지 이야기해 왔던 REFERENCE OBJECT의 추적성과 유일성을 만족시킬 수 없다.
- 생성된 주문 객체가 어플리케이션의 생명주기 동안 동일한 객체로 참조되기 위해서는 REPOSITORY가 필요하다.
- 생성된 객체는 REPOSITORY에 의해 관리되며 객체가 필요한 경우 REPOSITORY를 통해 해당 REFERENCE OBJECT를 얻을 수 있게 된다.
- 물론 REPOSITORY를 통해 등록된 객체와 조회된 객체의 식별자(identity)는 동일해야 한다. 테스트를 추가하자.
1234567891011121314public class OrderTest {public void testOrderIdentical() throws Exception {Order order = customer.newOrder("CUST-01-ORDER-01").with("상품1", 10).with("상품2", 20);orderRepository.save(order);Order anotherOrder = orderRepository.find("CUST-01-ORDER-01");assertEquals(order, anotherOrder);}}cs - 테스트는 통과한다.
- REPOSITORY를 통해 등록된 주문 객체들은 추적성과 유일성이라는 REFERENCE OBJECT 본연의 특성을 만족시키고 있다.
- 이번에는 생성된 주문 내역을 삭제하는 시나리오를 구현해 보자.
- 주문의 생명 주기를 관리하는 클래스는 OrderRepository이므로 여기에 삭제 관련 메서드를 추가하자.
- 먼저 OrderRepositoryTest 클래스에 주문을 삭제하는 테스트를 추가하자.
1234567891011121314151617public class OrderTest {public void testDeleteOrder() throws Exception {orderRepository.save(customer.newOrder("CUST-01-ORDER-01").with("상품1", 5).with("상품2", 20);Order order = orderRepository.find("CUST-01-ORDER-01");orderRepository.delete("CUST-01-ORDER-01");assertNull(orderRepository.find("CUST-01-ORDER-01"));assertNotNull(order);}}cs - 주문한 건을 생성한 후 REPOSITORY에 등록한다.
- 등록이 성공하면 해당 주문을 REPOSITORY로부터 삭제한다. 삭제 역시 조회의 경우와 마찬가지로 삭제할 주문 객체를 명시하기 위해 주문 ID를 인자로 전달한다.
- REPOSITORY에서 삭제되었다는 것을 확인하기 위해 주문 객체를 조회한 후 결괏값이 null인지를 확인한다.
- 테스트 메서드는 REPOSITORY에서 delete() 메소드를 호출하여 주문 객체를 삭제하기 전에 find() 메소드를 호출하여 REPOSITORY로부터 생성된 객체에 대한 참조를 보관한다.
- delete() 메소드를 호출하여 REPOSITORY로부터 해당 객체를 삭제한 후 앞에서 조회한 객체가 null이 아닌지를
- REPOSITORY 관점에서의 삭제는 더 이상 해당 객체를 REFERENCE OBJECT로 취급하지 않겠다는 것을 의미한다.
- 즉, 시스템이 해당 주문 객체의 추적성을 보장하지 않겠다는 의미이다.
- 따라서 일단 삭제가 완료되면 더 이상 REPOSITORY를 통해 해당 객체를 얻을 수 없게 된다.
- 이것을 주문 객체 자체의 소멸과 혼동해서는 안 된다.
- 단지 주문 객체가 REPOSITORY의 제어에서 벗어나 추적성과 유일성을 잃을 뿐이지 객체 자체가 소멸되는 것은 아니다.
- 객체의 소멸은 가비지 컬렉터에 의해서만 가능하다.
- REPOSITORY에 삭제 메서드를 추가하기 위해 Registrar에 삭제와 관련된 기본 기능을 추가하자.
- 각 ENTRY POINT 클래스와 연관된 Map으로부터 검색 키의 엔트리를 삭제한 후 반환하도록 구현한다.
1234567891011121314public class Registrar {public static EntryPoint delete(Class<?> entryPointClass,String objectName) {return soleInstance.deleteObj(entryPointClass, objectName);}@SuppressWarnings("unused")private EntryPoint deleteObj(Class<?> entryPointClass, String objectName) {Map<String,EntryPoint> theEntryPoint =entryPoints.get(entryPointClass);return theEntryPoint.remove(objectName);}}cs - Registrar를 사용하여 OrderRepository에 주문 삭제 메소드를 추가한다.
1234567public class OrderRepository {public Order delete(String identity) {return (Order)Registrar.delete(Order.class, identity);}}cs - 테스트는 통과한다.
- 시스템은 REFERENCE OBJECT를 정상적으로 추적하고 있으며, REPOSITORY는 객체의 생명 주기를 관리하기 위해 필요한 모든 기능을 제공하고 있다.
참조
이터니티 - Domain-Driven Design의 적용
'JAVA > DDD' 카테고리의 다른 글
DDD - Dependency Injection과 AOP 3부 (0) 2021.01.22 DDD - Dependency Injection과 AOP 2부 (0) 2021.01.22 AGGREGATE와 REPOSITORY 5부 (0) 2021.01.08 AGGREGATE와 REPOSITORY 4부 (0) 2021.01.05 Aggregate와 Repository 3부 (0) 2020.12.30