Domain Driven Design Ch7

3,221 views

Published on

Domain Driven Design Ch7

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,221
On SlideShare
0
From Embeds
0
Number of Embeds
1,368
Actions
Shares
0
Downloads
0
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Domain Driven Design Ch7

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

×