Ce diaporama a bien été signalé.
Nous utilisons votre profil LinkedIn et vos données d’activité pour vous proposer des publicités personnalisées et pertinentes. Vous pouvez changer vos préférences de publicités à tout moment.

DDD 준비 서문래

4 036 vues

Publié le

DDD를 시작하기전 몸 풀기

Publié dans : Logiciels
  • Identifiez-vous pour voir les commentaires

DDD 준비 서문래

  1. 1. DDD 준비 @서문래 최범균(madvirus@madvirus.net), 2017-01-20
  2. 2. 발표자 • 최범균,madvirus@madvirus.net • 주로자바로먹고살며,잡다한분야에관심 • 코딩잘하고,글잘쓰고싶은개발자 2DDD 소개@서문래
  3. 3. 내용 • 내용 • 언어,모델 • 아키텍처 • DDD소개 • 대상 • DDD에관심있는개발자 3DDD 소개@서문래
  4. 4. # 언어와 모델 • 언어 • 도메인 • 모델 4DDD 소개@서문래
  5. 5. 언어 • 언어매우중요 • 일관되지않은언어사용개발지옥 • 완전히다른단어,(거의)같은것 • (거의)같은 단어,미세한의미차이 • 일관된사용해석불필요비용감소 • 코드,문서,대화에서일관된언어사용 • 유비쿼터스언어(UbiquitousLanguage) 5DDD 소개@서문래
  6. 6. 이해하기 쉬운 용어 • 향후코드이해에유리한용어선택 • 가능하면간결한단어 • 어려운영어,모호한영어<발음대로영어표기 • 예:CivilAppeal<Minwon • 예:Kind,Type,Class,…<Gubun 6DDD 소개@서문래
  7. 7. 언어 = 도메인, 컨텍스트 • 언어는특정도메인이나컨텍스트에서의미 • 같은대상도도메인에따라다른용어 • 카탈로그의고객,주문의구매자 • 같은용어도도메인에따라다른의미 • 배송의상품은실물,카탈로그의상품은정보 • 도메인에맞는용어선택에는노력필요 • 도메인전문과와의대화 • 대화에출현한단어와(레거시)코드가불일치하면바꾸는노력 7DDD 소개@서문래
  8. 8. 언어의 발전 • 도메인에대한새로운이해 • 코드에반영,언어에반영 public class Order { public voidchangeShippingInfo( ShippingInfonewShippingInfo){ if (state== OrderState.PAYMENT_WAITING|| state== OrderState..PREPARING){ this.shippingInfo= newShippingInof; } else { thrownew IllegalStateException(); } } public class Order { public voidchangeShippingInfo( ShippingInfonewShippingInfo){ verifyNotYetShipped(); this.shippingInfo = newShippingInof; } public voidcancel() { verifyNotYetShipped(); this.state= OrderState.CANCELED; } privatevoid verifyNotYetShipped(){ if (state!= OrderState.PAYMENT_WAITING&& state!= OrderState.PREPARING) throw new IllegalStateException(); } "결제대기거나물건준비중일때" 배송지를변경할수있다. "배송을아직하지않았으면" 배송지를변경할수있다,주문을취소할수있다. 8DDD 소개@서문래
  9. 9. 추상화 • 개념이나표현을정의하는과정 • 구체적인용어 • 요구사항과대화속에서도출 • 이해가깊어지면새로운개념발견 • 추상화언어정의 • 알맞은언어선택을할수있도록도와줌 9DDD 소개@서문래
  10. 10. 도메인Domain 소프트웨어로 해결할 문제 영역 온라인 쇼핑 10DDD 소개@서문래
  11. 11. 도메인 모델 • 도메인의구성요소를개념적으로표현한것 • 도메인의이해를바탕으로만들어짐 • 구현과정에서점진적으로상세화 • 소프트웨어모델 • 다양한관점에서의모델 • 정적인모델 • 클래스다이어그램,ER다이어그램 • 동적인모델 • 커뮤니케이션다이어그램,시퀀스다이어그램 • 상태다이어그램 11DDD 소개@서문래
  12. 12. 단순 데이터 모델 • 일부개념이사라짐 • 단순속성나열 • 반쪽 • 중요도메인기능/제약 이드러나지않음 Order ------------------------ -id:String -state:String -totalAmounts:int -receiverName:String -receiverPhoneNumber;String -shipAddr1:String -shipAddr2:String -shipZipcode:String -ordererName:String -ordererId:String Product -------------- -id:String -listPrice:int 12DDD 소개@서문래
  13. 13. 도메인 모델에 필요한 것 • 표현력 • 최대한중요개념이드러나도록 • 도메인용어를최대한모델에반영 • 불필요한번역/해석이발생하지않도록모델구축 • 기능 • 객체모델,함수 • 동적측면/제약조건표현 • 상태다이어그램등을활용 13DDD 소개@서문래
  14. 14. 도메인 모델 예 14DDD 소개@서문래
  15. 15. 도메인 모델 예 15DDD 소개@서문래
  16. 16. # 아키텍처 • 영역 • DIP • 인프라 16DDD 소개@서문래
  17. 17. 연통 배관 * 계층 구조 UI 서비스 DAO UI 서비스 DAO UI 서비스 DAO • 참고 자료: http://www.slideshare.net/gyumee/ss-55616001 17DDD 소개@서문래
  18. 18. 고수준 모듈, 저수준 모듈 • 고수준모듈 • 어떤의미있는 단일기능을 제공하는모듈 • 저수준모듈 • 고수준모듈의 기능을구현하기 위해필요한 하위기능의 실제구현 18DDD 소개@서문래
  19. 19. 의존의 방향 • 고수준저수준으로의존 • 저수준의변경에따라고수준이영향을받을수있음 19DDD 소개@서문래
  20. 20. DIP(Dependency Inversion Principle) • 고수준모듈이저수준모듈에의존하지않고 저수준모듈이고수준모듈에의존함 • 의존방향을"고수준저수준"에서 "저수준고수준"으로변경 고수준 저수준 20DDD 소개@서문래
  21. 21. DIP 주의사항 21DDD 소개@서문래
  22. 22. (DDD의) 영역 영역 설명 표현(UI) 사용자의 요청을 해석해서 다른 영역에 처리를 위임하고, 결과를 사용자에 알맞게 변환해서 전달한다. 사용자에게 보여지는 흐름을 제어한다. 응용 사용자의 요청을 처리할 기능을 구현한다. 처 리 흐름을 구현하며, 도메인 로직은 도메인 영 역에 위임한다. 도메인 도메인 로직을 구현한다. 엔티티, 밸류, 애그리 거트, 리포지토리, 도메인 서비스가 위치한다. 인프라스트럭처 DBMS 연동, REST 클라이언트와 같은 구현 기 술을 다룬다. DIP 구조에서 저수준 모듈이 이 영역에 위치한다. 22DDD 소개@서문래
  23. 23. 아키텍처에서의 고수준/저소준 23DDD 소개@서문래
  24. 24. DIP와 DDD의 하위 도메인 간 연결 도메인 인프라UI/응용 도메인 인프라UI/응용 24DDD 소개@서문래
  25. 25. 인프라스트럭처 • 기반구현기술제공 • 응용서비스나도메인서비스구현 • 외부시스템과의연동이나특정기술에기반한구현 • 리포지토리구현 • 외부RESTAPI연동구현 • 메일클라이언트연동구현 • DIP고려 • 응용이나도메인에위치할인터페이스를구현관점에서 작성하지않도록함 25DDD 소개@서문래
  26. 26. # DDD 소개 • 도메인모델구성요소 26DDD 소개@서문래
  27. 27. 세 개의 축 Language Bounded Context Tactical Pattern 27DDD 소개@서문래
  28. 28. DDD의 모델 구성 요소 • 기본모델 • 엔티티 • 밸류 • 모델의묶음 • 애그리거트 • 기능 • 객체모델 • 도메인서비스 • 영속 • 리포지토리 28DDD 소개@서문래
  29. 29. 엔티티로 중심 모델 만들기 • 엔티티의특징 • 식별자(ID)를가짐 • 식별자는바뀌지않음 • 데이터/상태를가짐 • 자기만의라이프사이클을가짐 • (주로)엔티티의상태에따라다르게동작함 • (주로)DB테이블과1대1관계를가짐 • 대표적인예 • 주문,회원,제품 29DDD 소개@서문래
  30. 30. 밸류로 모델 표현력 증가하기 • 개념적으로하나인데이터집합을표현 • 예,주소,배송지,돈 Order ------------------------ -id:String -state:String -totalAmounts:int -receiverName:String -receiverPhoneNumber;String -shipAddr1:String -shipAddr2:String -shipZipcode:String -ordererName:String -ordererId:String Address ------------------------ -addr1:String -addr2:String -zipcode:String Receiver ------------------------ -name:String -phoneNumber;StringShippingInfo ------------------- -receiver:Receiver -address:Address Order ------------------------ -id:String -state:String -totalAmounts:Money -shippingInfo:ShippingInfo -orderer:Orderer Money ---------- -value:int Orderer ---------- -name:String -id:String 30DDD 소개@서문래
  31. 31. 모델에 기능 넣기 • 메서드로기능과제약표현 public class Order { public void changeShippingInfo(ShippingInfo newShippingInfo) { verifyNotYetShipped(); setShippingInfo(newShippingInfo); } private void verifyNotYetShipped() { if (state != OrderState.PAYMENT_WAITING || … ) throw new IllegalArgumentException("already shipped"); } private void setShippingInfo(ShippingInfo si) { this.shippingInfo = si; } … 31DDD 소개@서문래
  32. 32. 도메인 모델의 공개 set 메서드 없애기 • 도메인코드의공개set/get메서드는 • 핵심개념이나의도를사라지게만듬 • completePayment()vssetOrderState() • 절차지향이되기쉬움 • verifyNotYetShipped()vsgetOrderState()!=WAITING_PAYMENT • set/get메서드기능/제약을표현하는메서드 • 필요한경우get과is같은데이터제공메서드사용 • 영역경계간데이터를전달하기위한DTO와구분 32DDD 소개@서문래
  33. 33. # 애그리거트 • 애그리거트란 • 애그리거트크기,경계 33DDD 소개@서문래
  34. 34. 애그리거트Aggregate로 모으기 • 개별모델을상위수준에서묶어주는단위 34DDD 소개@서문래
  35. 35. 애그리거트 • 상위수준에서모델을이해하는데도움 • 도메인규칙/일관성을관리하는단위 • 복잡한도메인을단순화 • 한애그리거트에속한객체는유사한라이프사이클 • 특히,함께생성되고함께삭제됨 35DDD 소개@서문래
  36. 36. 애그리거트와 경계 • 한애그리거트에속한객체는다른애그리거트에속 하지않음 • 함께변경되는객체는한애그리거트에속할가능성높음 • 예,배송지주소,주문정보(주문개수) • 한애그리거트는자기자신만관리 • 다른애그리거트를직접변경하지않음 • 예,주문애그리거트에서회원애그리거트를변경하지않음 36DDD 소개@서문래
  37. 37. 애그리거트 루트 • 애그리거트의책임자 • 애그리거트에속한 객체가일관된상태를 유지하도록관리하는 책임을가진객체 • 애그리거트의 모든객체는루트에 직/간접적으로속함 37DDD 소개@서문래
  38. 38. 애그리거트 루트 • 도메인규칙과일관성을유지하는주체 • 애그리거트루트를통해서만애그리거트내부상태변경 • 애그리거트외부에기능을제공 public class Order { public void changeShippingInfo(ShippingInfo newShippingInfo) { verifyNotYetShipped(); setShippingInfo(newShippingInfo); } private void verifyNotYetShipped() { if (state != OrderState.PAYMENT_WAITING || … ) throw new IllegalArgumentException("already shipped"); } private void setShippingInfo(ShippingInfo si) { this.shippingInfo = si; } … 38DDD 소개@서문래
  39. 39. 애그리거트 루트 • 애그리거트에속한객체를 이용해서기능구현 • 애그리거트에속한다른 객체에위임 • 애그리거트에속한다른 객체의상태를조회 • 밸류객체는객체자체를 교체하는방법이쉬움 public class Member { private Password password; public void changePassword( String curPw, String newPw) { if (!password.match(curPw)) { // 위임 throw new NotMatchingException(); } // 상태 변경 this.password = new Password(newPw); } … 39DDD 소개@서문래
  40. 40. 애그리거트의 크기, 경계 • 주로한개엔티티와소수의밸류객체로구성 • N개엔티티객체로구성되는경우는흔치않음 • has로보이는1-N관계는서로다른애그리거트인지 확인필요 • M-N관계는연관객체가어느애그리거트에속하는 지확인필요 40DDD 소개@서문래
  41. 41. # 리포지토리 • 리포지토리 • 스펙 • 리포지토리와애그리거트간연관 41DDD 소개@서문래
  42. 42. 리포지토리 인터페이스 • 리포지토리는애그리거트(루트)단위로존재 • OrderOrderRepository,MemberMemberRepository • 테이블단위로존재하는것이아님 • 리포지토리의주요메서드 • save(Userorder) • UserfindOne(Stringemail) • List<User>findByJoinDateBetween(Datefrom,Dateto) • List<User>findBySpec(Specificationspec) • Page<User>findByName(Stringname) • voidremove(Useruser) 42DDD 소개@서문래
  43. 43. 리포지토리 구현 기술: JPA • 애그리거트를RDBMS와매핑하기에무난한기술 • 애노테이션기반설정(XML보다자바설정선호) • 밸류에대한매핑설정/확장지원 • @Embeddable • AttributeConverter • 콜렉션지원 • 엔티티간1-1,1-N,N-M지원 • 연관엔티티에대한영속성전파 43DDD 소개@서문래
  44. 44. Spring Data JPA • 반복작업제거 • 인터페이스만만들면구현객체는런타임에생성 • 정해진규칙에따라인터페이스정의 • JPQL,네이티브쿼리실행가능 • 페이징,스펙(+QueryDSL)지원 44DDD 소개@서문래
  45. 45. 스펙 • 검색조건을표현 • 개별검색조건을하나의스펙으로표현 • 검색조건의의미가잘드러남 • And,Or를이용한스펙조합 • 주로조회기능에서활용 • SpringDataJPA스펙지원 • 설명:http://goo.gl/7R4SYO Specification<OnlineTest> nameSpec = nameLike("중간고사"); Specification<OnlineTest> yearSpec = yearBetween(2011, 2016); Specifications<OnlineTest> specs = where(nameSpec); specs = specs.and(yearBetween); List<OnlineTest> test = onlineRepository.findAll(specs); 45DDD 소개@서문래
  46. 46. 애그리거트는 완전체 • 리포지토리는완전한애그리거트를다룸 • 로딩시점에애그리거트에속한모든연관객체로딩 • 즉,엔티티나콜렉션에대해EAGER로딩기본사용 • 기능구현에따라안전한경우에한해LAZY로딩 • 저장시점에애그리거트에속한모든연관객체저장 • 삭제시점에애그리거트에속한모든연관객체삭제 save(onlineTest)   find(id) 46DDD 소개@서문래
  47. 47. 애그리거트 간 연관 • 애그리거트간연관:애그리거트루트를참조 • 직접참조보다는ID참조선호 • 몇가지이유 • 편한탐색오용방지 • 성능에대한고민제거 • 시스템확장시유리 public class Reserve { private Customer reserver; … public class Reserve { private CustomerId reserverId; … 47DDD 소개@서문래
  48. 48. 애그리거트 간 연관: ID 참조 • 연관된애그리거트와의조합이필요한기능 • 상태변경기능 • 응용서비스에서조합처리 • 도메인서비스고려 • 여러애그리거트함께조회 • 조회전용DAO고려 48DDD 소개@서문래
  49. 49. 사고 방식 추가 • 조인,데이터애그리거트,기능 • 애그리거트단위로기능구현 • 기능에서객체의상태를변경 • 루트에서다른객체에위임 • 한개모델조회,명령 • CQRS • 조회전용DTO/DAO 49DDD 소개@서문래
  50. 50. # 응용 서비스 50DDD 소개@서문래
  51. 51. 응용 서비스 • 사용자가요청한기능을실행하고결과를리턴 • 일종의입력과출력을가진프로시저 • 도메인을사용해서기능구현 • 도메인영역과표현영역을연결해주는창구 51DDD 소개@서문래
  52. 52. 응용 서비스의 구현 • 도메인객체를사용한기능구현 • 리포지토리에서애그리거트루트를구하고 • 애그리거트루트의기능을실행 @Service public class ActivateOperatorService { private OperatorRepository operatorRepository; @Transactional public void activate(String operatorId) { // 애그리거트 루트를 구하고 Operator operator = operatorRepository.findOne(operatorId); if (operator == null) throw new OperatorNotFoundException(); // 도메인 기능을 실행 operator.activate(); } 52DDD 소개@서문래
  53. 53. 응용 서비스의 입력과 출력 • 메서드파라미터 • 기능을수행하는데필요한데이터 public class ChangePasswordService { public void changePassword( String id, String curPw, String newPw) { Member member = findMember(id); member.changePassword(curPw, newPw); } ... public class PlaceOrderService { public OrderNo placeOrder(OrderRequest req) { OrderNo id = createNextOrderNo(); Order order = createOrder(id, req); orderRepository.save(order); return id; } ... * 도메인의 엔티티를 서비스의 파라미터로 사용하지 말 것 * 딱 들어맞는 경우로만 제한 53DDD 소개@서문래
  54. 54. 응용 서비스의 입력과 출력 • 리턴 • 요청처리결과를사용자에게보여줄경우응답제공 • 예,새로생성한주문의ID,주문목록 • 고민거리 • 도메인객체리턴vsUI에노출할데이터만담은객체리턴 • 구현편의성,팀표준,성능등고려 • 조회전용모델검토 • 익셉션은실패를의미 • 응용서비스나도메인에서발생 54DDD 소개@서문래
  55. 55. 응용 서비스와 트랜잭션 • 트랜잭션담당 public class ChangePasswordService { @Transactional public void changePassword(ChangePasswordRequest request) { Member member = findExistingMember(request.getMemberId()); member.changePassword( request.getCurrentPassword(), request.getNewPassword()); } ... * 고민거리 : Open Session In View 55DDD 소개@서문래
  56. 56. 응용 서비스에 도메인 로직 넣지 않기 • 도메인로직이나제약을구현하면안됨! public class ActivateOperatorService { private OperatorRepository operatorRepository; @Transactional public void activate(String operatorId) { Operator operator = operatorRepository.findOne(operatorId); if (operator == null) throw new OperatorNotFoundException(); if (operator.isDeleted() || operator.isActivated()) { throw new RuntimeException(); } operator.setActivated(true); // operator.activate()에 들어가야 할 제약 검사 } 56DDD 소개@서문래
  57. 57. 몇 가지 고민할 것 • 서비스 • 크기 • 도메인단위로기능몰아넣기vs기능군별로서비스구분 • 인터페이스필요여부 • 인터페이스 +구현클래스 • 그냥클래스만 • 서비스vs표현vs도메인 • 값검증 • 권한검사 57DDD 소개@서문래
  58. 58. # BOUNDED CONTEXT 58DDD 소개@서문래
  59. 59. BOUNDED CONTEXT • 모델경계=언어경계 • 문백안에서의미 • 논리적인언어경계선 • 기능을제공하는물리 적인시스템 • 조직구조를따라하위 도메인에매핑 59DDD 소개@서문래
  60. 60. BOUNDED CONTEXT의 구조 60DDD 소개@서문래
  61. 61. BC 간 관계 • 서비스공급자,고객 • 상류,하류 • 공개호스트서비스 OpenHostService • 안티코럽션레이어 AnticorruptionLayer • 공유커널 SharedKernel • 독립방식 SeparateWay 61DDD 소개@서문래
  62. 62. 컨텍스트 맵 62DDD 소개@서문래
  63. 63. # 정리 63DDD 소개@서문래
  64. 64. 끝 최범균 | madvirus@madvirus.net | http://javacan.tistory.com 64DDD 소개@서문래

×