2. Cargo Shipping 시스템 기본 요구사항 customer 의 cargo 를 추적할 수 있어야 한다. 미리 cargo를 예약할 수 있어야 한다. cargo가 특정 handling 상태가 되면 customer 에게 자동으로 invoice 를 보내야 한다. 여기까지 요구사항만 놓고 design 을 만들어보자.
5. model 에 나오는 객체들 Handling Event Cargo 를 배에 싣고 내리는 등의 행위 loading, unloading, being claimed 등의 상위클래스일 수 있음 Delivery Specification 목적지, 도착날짜 Cargo 에게 맡기지 않고 따로 객체를 만든 이유 Cargo 객체가 복잡해진다 LoD(Level of Detail) 을 제공 Cargo 의 목적이 Delivery Specification 이라는 걸 좀 더 분명하게 얘기할 수 있다.
6. model 에 나오는 객체들 role Customer 의 역할(shipper, receiver, payer…) 한 Customer 는 특정 Cargo 에 대해서 하나의 role 만 맡는다(Qualified Association) string 또는 클래스로 구현 Carrier Movement 특정 Carrier(트럭, 화물선) 가 Cargo 를 하나의 Location 에서 다른 Location 으로 이동시키는 행위
7. model 에 나오는 객체들 Delivery History Cargo 에 어떤 일이 있었는지에 대한 기록 Delivery History 는 마지막 Carrier Movement 로 Cargo 의 현재위치를 계산할 수 있다. 각 객체를 어떻게 찾고 저장할지를 model 에서는 다루지 않지만 design 에서는 다뤄야 한다.
8. Application 도입 Applications Tracking Query Booking Application Incident Logging Application application 은 coordinator 이다. 질문에 대한 답을 하는 것은 domain layer 영역
9. Entity,Value Object 구분 Customer : 고객, 회사이므로 상식적으로 Entity TaxID는 ID 로 부적합. Customer 는 처음 sales contact 때 ID 를 부여하고 있더라. Cargo : Entity. 실제 각 물류회사에는 Cargo 별로 ID 부여 Handling Event : Cargo 를 추적할 수 있어야 하므로 Entity Cargo ID, completion time, type 조합으로 ID 생성 Carrier Movement : shipping schedule 코드로 identity 가능 Location : 다른 지역이 이름만 같을 수 있으므로 unique ID 필요 자동증가 id 정도면 충분 Delivery History : 교환할 수 없으므로 entity. 각 Cargo 별로 History 가 다르니까 Delivery History 는 Cargo 와 1:1 관계임. AGGREGATES 관계로 가자(Cargo 의 멤버변수) Delivery Specification 은 두 Cargo 가 같은 장소로 동시에 배송될 수 있으므로 VALUE OBJECTS Role 은 연계관계이지만, 지속성은 필요없음. VALUE OBJECT stamps, names 도 VALUE OBJECT 모델에서 중요하지 않은 속성은 대문자를 부여받지 못함 대문자를 받은 model language 는 위키페이지의 이름으로 쓸 수 있다
10. Shipping 에서 association 디자인 상호참조는 최대한 피하자 Customer 가 Cargo 를 레퍼런스하면귀찮을 수 있음 Customer 가 Cargo 한정으로 사용되는 것도 아님 특정한 배(Carrier)의 인벤토리를 추적한다면 Carrier Movement -> Handling Event 가 중요할 수 있지만 지금은 Cargo 만 추적하면 되므로 Handling Event -> Carrier Movement 필요 딱 필요한 만큼만 구현할 것 환형 관계가 하나(Cargo -> Delivery History -> Handling Events -> Cargo) 있는데 가능하면 피해라. 직접 레퍼런스가 싫다면 (DB 같이 query 가 가능한) REPOSITORY 를 이용하라.
11.
12. AGGREGATE 경계 Customer, Location, Carrier Movement 는 Cargo 가 공유하므로 AGGREGATE root 여야 함 Cargo 도 AGGREGATE 의 root 인데, 그 밑에 뭐가 들어갈 수 있을까? Delivery History 는 Cargo 를 통해서만 접근한다. Delivery Specification 는 VALUE OBJECT 니까 추가 Handling Events collection 이든 DB 든 Delivery History 에 대한 Handling Events 를 찾을 수 있어야 하고 특정 Carrier Movement 에 대해 load, unload 를 다 찾을 수 있어야 한다면, Handling Events 가 Cargo 에 독립적인 의미를 가지기 때문에 독자적인 AGGREGATE 의 root 가 되어야 한다. 따로 빼면 low-contention transaction 으로 만들 수 있다.
13.
14. REPOSITORY 선택 AGGREGATE root 가 아니면 REPOSITORY 가 필요없음(직접 접근할 일이 없으니까). Booking Application 에서는 여러 role (shipper, receiver, ...) 을 맡을 Customer 를 선택할 수 있어야 하므로 Customer Repository 가 필요하다. Cargo 의 도착지를 지정할 수 있어야 하므로 Location Repository 도 필요하다. Activity Logging Application 는 Cargo 를 실은 Carrier Movement 를 찾아볼 수 있어야 하므로 Carrier Movement Repository필요.
15.
16. Scenarios Customer 가 Cargo 의 목적지를 바꿀 수 있는가? Cargo 의 Delivery Specification 은 VALUE OBJECT 니까 덮어쓰면 된다. Repeat Business : 같은 Customer 에게 화물을 보낼 때 그전 정보를 prototype 으로 쓰고 싶다(yes24 의 이전배송정보 사용). Cargo 는 ENTITY 이자 AGGREGATE 의 root 이므로 주의 Delivery History : 비어있는 새 객체 생성 Customer Roles : Map (or collection) 을 key 와 함께 복사 key : 역할, value : Customer Tracking ID : 새로운 Tracking ID 를 제공한다. Cargo AGGREGATE 영역 밖에는영향을 미치지 않는다
17. 객체 생성 - Cargo public Cargo CopyPrototype(String newTrackingID) public Cargo newCargo(Cargo prototype, String newTrackingID) public Cargo newCargo(Cargo prototype) 비어있는 Delivery History 와 Delivery Specification 가 null 인 Cargo 를리턴 Delivery Specification 가 null 일 필요가 있을까? public Cargo(Stirng id) { trackingID = id; deliveryHistory = new DeliveryHistory(this); customerRoles = new HashMap();
18. 객체 생성 - Handling Event 추가 사용자는 Incident Logging Application 로 Handling Event 를 입력한다. Handling Event 는 ENTITY 이기 때문에 생성자에 identity 에 관련된 모든 attribute 가 다 들어와야 한다. Cargo ID + completion time + event type Handling Event 는 Cargo 를 handled 라는 멤버변수로 연관관계 public HandlingEvent(Cargo c, String eventType, Date timeStamp) { handled = c; // const 변수감 type = eventType; // const 변수감 completionTime = timeStamp; } public static HandlingEventnewLoading( Cargo c, CarrierMovementloadedOnto, Date timeStamp) { HandlingEvent r = new HandlingEvent(c, LOADING_EVENT, timeStamp); r.setCarrierMovement(loadedOnto); return r; }
20. 잠깐, 다른 디자인은 없을까? Handling Event 를 추가할 때 Delivery History 를 업데이트하려면 Cargo AGGREGATE 를 transaction 걸어야 한다. 다른 user 가 같은 Cargo 를 동시에 고친다면 transaction이 실패(낙관적인 lock)하거나 지연(비관적인 lock)될 수 있다. Handling Event 를 경쟁없이추가하려면 design 을 고쳐야 한다. Handling Event 를 Delivery History 의 collection 대신 query 로 바꾸면 Handling Event 를 AGGREGATE 정합성 문제없이 추가 가능 interference 없이 transaction 할 수 있다. Handling Event 의 입력이 많고 쿼리가 적다면 성능향상 가능. 그래서 Handling Event Repository 를 추가한다. findByCargoIDTimeType, findByCargoTrackingID, findByScheduleID, findMostRecentCargoIDType Handling Event Repository 에서 원하는 history 를 query 할 수 있기 때문에 Delivery History 에 persistent state 가 없어지므로 쓸모없게 된다. 다만, 하나의 Cargo 에 대한 전체 History 를 볼 일이 더 많을 경우라면 성능 trade-off 가 발생할 수 있다.
21.
22. 새로운 기능을 추가해보자 화물의 'type, 출발지, 목적지에 따른 화물량 예측' 기능을 Booking Application 에 통합해, 새로 booking 할 때 적합성 여부를 알고 싶다. Booking Application 은 Cargo Repository 와 Sales Management System 의 정보가 필요하다.
23. 두 시스템 연결 Sale Management System 은 별도의 소프트웨어(시스템) Interface 로 wrap 하기 보다는 중간에 번역 레이어를 두자 Anti Corruption Layer 번역 DB -> 서버 -> 클라이언트 -> 엔진 즐겨찾기DB -> XML 전송 -> IE, Firefox 변환
24. Segmenting the Business Cargo 의 type 을 정의하지 않았다. ENTERPRISE SEGMENT(Analysis Patterns) a set of dimensions that define a way of breaking down a business Enterprise Segment 라는 클래스(VALUE OBJECT)가 등장 날짜, 지역등으로구분짓는 데이터(일종의 where 절, 카테고리)
27. Allocation Checker Enterprise Segment 와 외부시스템의 카테고리 이름을 번역 SERVICE Booking Application 에서 하던 business rule 작업(Cargo 배치)과 Enterprise Segment 생성 책임을 Allocation Checker 으로 넘긴다. Allocation Checker 가 Enterprise Segments 로 만들 수 없는 차원은 Sales Management System 에서 쓸 수 없다는 단점이 있다
28. 성능 문제 Sales Management System 이 외부에 있다면? Allocation Checker 에 cache 한다. COM+ 에서 interface query 하는 방식도 있다
29. 새로운 기능 추가하기 - 정리 Sales Management System 을 Booking Application, Cargo Repository 와 통합하면서 복잡해졌지만, ANTICORRUPTION LAYER, SERVICE, ENTERPRISE SEGMENTS 를 도입하므로서 정돈되면서 도메인을 더 풍부하게 만들 수 있었다.
30. 정리 DDD 의 모델은 요리법(recipe) 처럼 만들어야 한다. 모델만 봐도 구현할 수 있게 하되, 모델을 있는 그대로 구현하는 것이 아니라 상황에 맞게 적용할 수 있는 유연함이 필요하다. DDD 의 모델은 패턴언어와 비슷하다. 모델은 계속 같은 언어로 표현되면서 다른 모델과 관계를 맺는다.