Rich domain model

1,035 views

Published on

Published in: Software
0 Comments
10 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,035
On SlideShare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
21
Comments
0
Likes
10
Embeds 0
No embeds

No notes for slide

Rich domain model

  1. 1. Object-Oriented Design & Architecture Rich Domain Model 조영호 Eternity’s Chit-Chat http://aeternum.egloos.com
  2. 2. 2. 데이터-지향 설계 3. 책임-주도 설계 목차 4. 아키텍처 & 프레임워크 1. 영화 예매 시스템 도메인 5. 결롞
  3. 3. 1. 영화 예매 시스템 도메인
  4. 4. 4 / 문서의 제목 온라인 영화 예매 시스템
  5. 5. 5 / 문서의 제목 Domain Concept - 영화 Movie
  6. 6. 6 / 문서의 제목 Domain Concept - 상영 2010-10-20 09:30 조조 Showing 2010-10-21 20:30 5회 2010-12-01 14:20 4회
  7. 7. 7 / 문서의 제목 Domain Concept – 할인 정책 Discount Amount Discount Percent Discount 8,000 - 800 = 7,200 8,000 – (8,000 * 0.1) = 7,200
  8. 8. 8 / 문서의 제목 Domain Concept – 할인 규칙 Rule Sequence Rule Time Rule 조조 상영인 경우 월요일 10:00 ~ 12:00 상영인 경우 목요일 18:00 ~ 21:00 상영인 경우 10회 상영인 경우
  9. 9. 9 / 문서의 제목 Domain Concept –할인 정책 + 할인 규칙 Movie Discount Rule 1 0..1 1 1..*
  10. 10. 10 / 문서의 제목 10회 상영인 경우 Domain Concept –할인 정책 + 할인 규칙 Movie Discount Rule 1 0..1 1 1..* 이끼 8000원 Amount DC 800원 조조 상영인 경우 월요일 10:00 ~ 12:00 상영인 경우 목요일 18:00 ~ 21:00 상영인 경우
  11. 11. 11 / 문서의 제목 10회 상영인 경우 Domain Concept –할인 적용 이끼 8000원 Amount DC 800원 조조 상영인 경우 월요일 10:00 ~ 12:00 상영인 경우 목요일 18:00 ~ 21:00 상영인 경우 상영정보 2010년 12월 23일 목요일 18:00 ~ 20:00(7회차)
  12. 12. 12 / 문서의 제목 10회 상영인 경우 Domain Concept –할인 적용 이끼 8000원 Amount DC 800원 조조 상영인 경우 월요일 10:00 ~ 12:00 상영인 경우 목요일 18:00 ~ 21:00 상영인 경우 상영정보 2010년 12월 23일 목요일 18:00 ~ 20:00(7회차) 7,200
  13. 13. 13 / 문서의 제목 Domain Concept –예매 Reservation 이끼 2010년 12월 23일 (목) 7회 6:00(오후) – 8:00(오후) 2명 16,000원 14,400원 제 목 상영 정보 인 원 정 가 결재 금액
  14. 14. 2. 데이터-지향 설계
  15. 15. 15 / 문서의 제목 무엇을 저장할 것인가 - 데이터
  16. 16. 16 / 문서의 제목 데이터 모델 MOVIE ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY RESERVATION ID CUSTOMER_ID(FK) SHOWING_ID(FK) FEE_AMOUNT FEE_CURRENCY AUDIENCE_COUNT RULE ID DISCOUNT_ID(FK) POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE DISCOUNT MOVIE_ID(FK) DISCOUNT_TYPE FEE_AMOUNT FEE_CURRENCY PERCENT SHOWING ID MOVIE_ID(FK) SEQUENCE SHOWING_TIME CUSTOMER ID CUSTOMER_ID NAME
  17. 17. 17 / 문서의 제목 또 다른 데이터 표현 – Anemic Domain Model Movie id title runningTime fee Reservation id customerId showingId Amounr audienceCount Rule id discountId position ruleType dayOfWeek startTime endTime sequence Discount movieId discountType amount percent Showing id movieId sequence showingTime Customer Id customerId name
  18. 18. 18 / 문서의 제목 초기 데이터 ID TITLE RUNNING_TIME FEE_AMOUNT FEE_CURRENCY 1 이끼 120 8000 KRW MOVIE MOVIE_ID DISCOUNTYPE FEE_AMOUNT FEE_ACURRENCY PERCENT 1 A 800 KRW NULL DISCOUNT ID DISCOUNT_ID POSITION RULE_TYPE DAY_OF_WEEK 1 1 0 S NULL RULE START_TIME NULL END_TIME NULL SEQUENCE 1 2 1 1 S NULL NULL NULL 10 3 1 2 T 2 10:00 12:00 NULL 4 1 3 T 5 18:00 21:00 NULL ID MOVIE_ID SEQUENCE SHOWING_TIME 1 1 7 2010-12-23 18:00 SHOWING
  19. 19. 19 / 문서의 제목 어떻게 처리할 것인가 - 프로세스
  20. 20. 20 / 문서의 제목 예매 처리 Service <<interface>> MovieDAO <<interface>> ReservationService reserveShowing(customerId, showingId, audienceCount) ReservationServiceImpl reserveShowing(customerId, showingId, audienceCount) <<interface>> DiscountDAO <<interface>> RuleDAO <<interface>> ShowingDAO <<interface>> ReservationDAO
  21. 21. 21 / 문서의 제목 데이터를 사용한 예매 프로세스 구현 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { ① 데이터베이스로부터 Movie와 Showing 정보 로딩 ② 데이터베이스로부터 Rule 정보 로딩 후 Showig에 적용할 수 있는 Rule 이 있는지 판단 ③ if (Rule 이 존재하면) { Discount를 읽어 요금 할인된 요금 계산 } else { Movie에 저장되어 있는 정액 요금 사용 } ④ Reservation 생성 후 데이터베이스 저장 } Algorithm or Process
  22. 22. 22 / 문서의 제목 데이터를 사용한 예매 프로세스 구현 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { Showing showing = showingDAO.selectShowing(showingId); Movie movie = movieDAO.selectMovie(showing.getMovieId()); ② 데이터베이스로부터 Rule 정보 로딩 후 Showig에 적용할 수 있는 Rule 이 있는지 판단 ③ if (Rule 이 존재하면) { Discount를 읽어 요금 할인된 요금 계산 } else { Movie에 저장되어 있는 정액 요금 사용 } ④ Reservation 생성 후 데이터베이스 저장 } ① 데이터베이스로부터 Movie와 Showing 정보 로딩
  23. 23. 23 / 문서의 제목 데이터를 사용한 예매 프로세스 구현 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { Showing showing = showingDAO.selectShowing(showingId); Movie movie = movieDAO.selectMovie(showing.getMovieId()); Rule rule = findRule(showing, movie); ③ if (Rule 이 존재하면) { Discount를 읽어 요금 할인된 요금 계산 } else { Movie에 저장되어 있는 정액 요금 사용 } ④ Reservation 생성 후 데이터베이스 저장 } private Rule findRule(Showing showing, Movie movie) { for(Rule each : ruleDAO.selectRules(movie.getId())) { if (each.isAccepted(showing, movie)) { return each; } } return null; } ② 데이터베이스로부터 Rule 정보 로딩 후 Showig에 적용할 수 있는 Rule 이 있는지 판단
  24. 24. 24 / 문서의 제목 데이터를 사용한 예매 프로세스 구현 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { Showing showing = showingDAO.selectShowing(showingId); Movie movie = movieDAO.selectMovie(showing.getMovieId()); Rule rule = findRule(showing, movie); Money fee = movie.getFee(); if (rule != null) { fee = calculateFee(movie); } ④ Reservation 생성 후 데이터베이스 저장 } ③ if (Rule 이 존재하면) { Discount를 읽어 요금 할인된 요금 계산 } else { Movie에 저장되어 있는 정액 요금 사용 } private Money calculateFee(Movie movie) { Discount discount = discountDAO.selectDiscount(movie.getId()); if (discount.isAmountType()) { return movie.getFee().minus(Money.wons(discount.getFee())); } else if (discount.isPercentType()) { return movie.getFee().minus( movie.getFee().times(discount.getPercent())); } return movie.getFee(); }
  25. 25. 25 / 문서의 제목 데이터를 사용한 예매 프로세스 구현 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { Showing showing = showingDAO.selectShowing(showingId); Movie movie = movieDAO.selectMovie(showing.getMovieId()); Rule rule = findRule(showing, movie); Money fee = movie.getFee(); if (rule != null) { fee = calculateFee(movie); } Reservation result = makeReservation(customerId, showingId, audienceCount, fee); reservationDAO.insert(result); return result; } ④ Reservation 생성 후 데이터베이스 저장 private Reservation makeReservation(int customerId, int showingId, int audienceCount, Money payment) { Reservation result = new Reservation(); result.setCustomerId(customerId); result.setShowingId(showingId); result.setAudienceCount(audienceCount); result.setFee(payment); return result; }
  26. 26. 26 / 문서의 제목 중앙 집중식Centralized 제어 스타일 reserveShowing() showing = selectShowing() rules = selectRules() isAccepted()* discount = selectDiscount() isAmountType() getFee() new rules :Rule :Showing DAO showing :Showing :Reservation Service :Rule DAO :Discount DAO discount :Discount :Reservation
  27. 27. 27 / 문서의 제목 아키텍처 패턴 Transaction Script
  28. 28. 3. 책임-주도 설계
  29. 29. 29 / 문서의 제목 책임Responsibility
  30. 30. 30 / 문서의 제목 Showing 상영 정보를 알고 있다 예매 생성 책임 예매 생성에 필요한 정보의 EXPERT에게 할당Creator 예매 정보를 생성한다
  31. 31. 31 / 문서의 제목 가격 계산 책임 영화 가격 정보를 알고 있는 EXPERT에 할당Information Expert Showing 상영 정보를 알고 있다 예매 정보를 생성한다 Movie Customer Movie 영화정보를 알고 있다 가격을 계산한다
  32. 32. 32 / 문서의 제목 Showing 상영 정보를 알고 있다 Movie 영화정보를 알고 있다 DiscountStrategy 할인율 계산 책임 할인율을 적용할 STRATEGY 객체 추가 DiscountStrategy 할인율 정책을 알고 있다 할인된 가격을 계산한다 예매 정보를 생성한다 Movie Customer 가격을 계산한다
  33. 33. 33 / 문서의 제목 Showing 상영 정보를 알고 있다 Movie 영화정보를 알고 있다 DiscountStrategy DiscountStrategy 할인율 정책을 알고 있다 할인된 가격을 계산한다 예매 정보를 생성한다 Movie Customer 가격을 계산한다 할인 여부를 판단할 책임 할인 정책을 판단하기 위한 SPECIFICATION 객체 추가 Rule 할인 정책을 알고 있다 할인 여부를 판단한다 Rule Showing
  34. 34. 34 / 문서의 제목 Rich Domain Model Customer Showing reserve(customer, count):Reservation Rule isStatisfiedBy(showing):boolean Reservation AmountStrategy PercentStrategy NonDiscountStrategy SequenceRule TimeOfDayRule DiscountStrategy calculateFee(showing):Money Movie calculateFee(showing):Money 상속inheritance과 다형성polymorphism의 활용 <<create>>
  35. 35. 35 / 문서의 제목 데이터에 대한 걱정은 잠시 꺼두셔도 좋습니다
  36. 36. 36 / 문서의 제목 public class Showing { public Reservation reserve(Customer customer, int audienceCount) { return new Reservation(customer, this, audienceCount); } } public class Reservation { public Reservation(Customer customer, Showing showing, int audienceCount) { this.customer = customer; this.showing = showing; this.fee = showing.calculateFee().times(audienceCount); this.audienceCount = audienceCount; } } public class Showing { public Money calculateFee() { return movie.calculateFee(this); } } public class Movie { public Money calculateFee(Showing showing) { return discountStrategy.calculateFee(showing); } } 책임 기반 구현
  37. 37. 37 / 문서의 제목 public abstract class DiscountStrategy { public Money calculateFee(Showing showing) { for(Rule each : rules) { if (each.isStatisfiedBy(showing)) { return getDiscountedFee(showing); } } return showing.getFixedFee(); } abstract protected Money getDiscountedFee(Showing showing); public abstract class Rule { abstract public boolean isStatisfiedBy(Showing showing); } public class SequenceRule extends Rule { public boolean isStatisfiedBy(Showing showing) { return showing.isSequence(sequence); } } public class TimeOfDayRule extends Rule { public boolean isStatisfiedBy(Showing showing) { return showing.isPlayingOn(dayOfWeek) && Interval.closed(startTime, endTime) .includes(showing.getPlayngInterval()); } } 책임 기반 구현
  38. 38. 38 / 문서의 제목 public abstract class DiscountStrategy { public Money calculateFee(Showing showing) { for(Rule each : rules) { if (each.isStatisfiedBy(showing)) { return getDiscountedFee(showing); } } return showing.getFixedFee(); } abstract protected Money getDiscountedFee(Showing showing); 책임 기반 구현 public class AmountDiscountStrategy extends DiscountStrategy { protected Money getDiscountedFee(Showing showing) { return showing.getFixedFee().minus(discountAmount); } } public class NonDiscountStrategy extends DiscountStrategy { protected Money getDiscountedFee(Showing showing) { return showing.getFixedFee(); } } public class PercentDiscountStrategy extends DiscountStrategy { protected Money getDiscountedFee(Showing showing) { return showing.getFixedFee().minus(showing.getFixedFee().times(percent)); } }
  39. 39. 39 / 문서의 제목 위임식delegated, 분산식dispersed 제어 스타일 :Reservation :Movie reserve() new :Showing calculateFee() calculateFee() :DiscountStrategy :Rule calculateFee() isStatisfied()*
  40. 40. 40 / 문서의 제목 아키텍처 패턴 Domain Model
  41. 41. 41 / 문서의 제목 Transaction Script의 단점 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { Showing showing = showingDAO.selectShowing(showingId); Movie movie = movieDAO.selectMovie(showing.getMovieId()); Rule rule = findRule(showing, movie); Money fee = movie.getFee(); if (rule != null) { fee = calculateFee(movie); } Reservation result = makeReservation(customerId, showingId, audienceCount, fee); reservationDAO.insert(result); return result; } private Money calculateFee(Movie movie) { Discount discount = discountDAO.selectDiscount(movie.getId()); if (discount.isAmountType()) { return movie.getFee().minus(Money.wons(discount.getFee())); } else if (discount.isPercentType()) { return movie.getFee().minus( movie.getFee().times(discount.getPercent())); } return movie.getFee(); } 새로운 할인 정책 추가
  42. 42. 42 / 문서의 제목 @Override public Reservation reserveShowing(int customerId, int showingId, int audienceCount) { Showing showing = showingDAO.selectShowing(showingId); Movie movie = movieDAO.selectMovie(showing.getMovieId()); Rule rule = findRule(showing, movie); Money fee = movie.getFee(); if (rule != null) { fee = calculateFee(movie); } Reservation result = makeReservation(customerId, showingId, audienceCount, fee); reservationDAO.insert(result); return result; } private Money calculateFee(Movie movie) { Discount discount = discountDAO.selectDiscount(movie.getId()); if (discount.isAmountType()) { return movie.getFee().minus(Money.wons(discount.getFee())); } else if (discount.isPercentType()) { return movie.getFee().minus( movie.getFee().times(discount.getPercent())); } else if (discount.isMilleageType()) { return movie.getFee().minus( movie.getMileageBaseAmount().times(discount.getMileageFactor())); } return movie.getFee(); } 기졲 코드 수정 Transaction Script의 단점
  43. 43. 43 / 문서의 제목 Rich Domain Model의 장점 Customer Showing reserve(customer, count):Reservation Rule isStatisfiedBy(showing):boolean Reservation AmountStrategy PercentStrategy NonDiscountStrategy SequenceRule TimeOfDayRule DiscountStrategy calculateFee(showing):Money Movie calculateFee(showing):Money <<create>> 새로운 할인 정책 추가
  44. 44. 44 / 문서의 제목 Rich Domain Model의 장점 Customer Showing reserve(customer, count):Reservation Rule isStatisfiedBy(showing):boolean Reservation AmountStrategy PercentStrategy NonDiscountStrategy SequenceRule TimeOfDayRule DiscountStrategy calculateFee(Showing):Money Movie calculateFee(showing):Money <<create>> MileageStrategy OCPOpen-Closed Principle
  45. 45. 4. 아키텍처 & 프레임워크
  46. 46. 46 / 문서의 제목 Layered Architecture User Interface Service Domain Infrastructure
  47. 47. 47 / 문서의 제목 도메인 레이어 캡슐화
  48. 48. 48 / 문서의 제목 Layered Architecture User Interface Service Domain Infrastructure
  49. 49. 49 / 문서의 제목 Service Layer  애플리케이션 경계  도메인 레이어의 재사용성 촉짂 <<interface>> ReservationService reserveShowing(customerId, showingId, audienceCount) ReservationServiceImpl reserveShowing(customerId, showingId, audienceCount) <<interface>> CustomerRepository <<interface>> ShowingRepository <<interface>> ReservationRepository Showing
  50. 50. 50 / 문서의 제목 Service Layer  Operation Script  Not Transaction Script <<interface>> ReservationService reserveShowing(customerId, showingId, audienceCount) ReservationServiceImpl reserveShowing(customerId, showingId, audienceCount) <<interface>> CustomerRepository <<interface>> ShowingRepository <<interface>> ReservationRepository Showing @Override @Transactional(propagation=Propagation.REQUIRED) public Reservation reserveShowing(int reserverId, int showingId, int audienceCount) { Customer reserver = customerRepository.find(reserverId); Showing showing = showingRepository.find(showingId); Reservation reservation = showing.reserve(reserver, audienceCount); reservationRepository.save(reservation); return reservation; }
  51. 51. 51 / 문서의 제목 도메인 레이어 의졲성 관리 User Interface Service Domain Infrastructure
  52. 52. 52 / 문서의 제목 순수한 객체 POJO Plain Old Java Object
  53. 53. 53 / 문서의 제목 POJO의 3대 요소 Dependency Injection Aspect-Oriented Programming Annotation
  54. 54. 54 / 문서의 제목 비침투적인Non-Intrusive 프레임워크  POJO 개발을 위한 젂제조건  Lightweight Framework
  55. 55. 55 / 문서의 제목 Dependency Injection  객체 간의 의졲성 관리 이슈로부터 도메인 레이어 보호  구성-사용 분리 원리the principle of separating configuration from use <<interface>> ReservationService reserveShowing(customerId, showingId, audienceCount) ReservationServiceImpl reserveShowing(customerId, showingId, audienceCount) <<interface>> CustomerRepository CustomerRepositoryImpl
  56. 56. 56 / 문서의 제목 Dependency Injection – Spring <bean id="reservationService” class="org.eternity.theater.reservation.ReservationServiceImpl"> <property name="customerRepository" ref="customerRepository"/> ...... </bean> <bean id="customerRepository“ class="org.eternity.theater.customer.hibernate.CustomerRepositoryImpl"> <property name="sessionFactory" ref="sessionFactory"/> </bean> <<interface>> ReservationService reserveShowing(customerId, showingId, audienceCount) ReservationServiceImpl reserveShowing(customerId, showingId, audienceCount) <<interface>> CustomerRepository CustomerRepositoryImpl
  57. 57. 57 / 문서의 제목 Impedance Mismatch  객체 모델과 DB 스키마 간의 불일치  객체 모델과 DB 스키마 간의 변환 계층 필요 RULE ID DISCOUNT_ID(FK) POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE DISCOUNT MOVIE_ID(FK) DISCOUNT_TYPE FEE_AMOUNT FEE_CURRENCY PERCENT Rule Amount Strategy Percent Strategy NonDiscount Strategy Sequence Rule TimeOfDay Rule DiscountStrategy
  58. 58. 58 / 문서의 제목 DATA MAPPER  객체 모델과 DB 스키마 간의 독립성 유지  도메인 객체는 DB에 대해 독립적 RULE ID DISCOUNT_ID(FK) POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE Rule SequenceRule TimeOfDayRule RuleMapper insert Update delete
  59. 59. 59 / 문서의 제목 O/R MAPPER - Hibernate <hibernate-mapping package="org.eternity.theater.movie.pricing" default-access="field"> <class name="Rule" table="RULE"> <id name="id" column="ID" type="long"> <generator class="native"/> </id> <discriminator column="RULE_TYPE" type="string" /> <subclass name="SequenceRule" discriminator-value="S"> <property name="sequence" column="SEQUENCE" type="integer"/> </subclass> <subclass name="TimeOfDayRule" discriminator-value="A"> <property name="dayOfWeek" column="DAY_OF_WEEK" type="integer"/> <property name="startTime" column="START_TIME" type="org.eternity.support.hibernate.TimeOfDayUserType"/> <property name="endTime" column="END_TIME" type="org.eternity.support.hibernate.TimeOfDayUserType"/> </subclass> </class> </hibernate-mapping> RULE ID DISCOUNT_ID(FK) POSITION RULE_TYPE DAY_OF_WEEK START_TIME END_TUME SEQUENCE Rule SequenceRule TimeOfDayRule
  60. 60. 60 / 문서의 제목 트랜잭션 경계 User Interface Service Domain Infrastructure Begin TX Commit Rollback
  61. 61. 61 / 문서의 제목 AOPAspect Oriented Programming User Interface Service Domain Infrastructure Begin TX Commit Rollback ASPECT
  62. 62. 62 / 문서의 제목 Spring AOP & Annotation User Interface Service Domain Infrastructure Begin TX Commit Rollback ASPECT <tx:annotation-driven/> <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <property name="sessionFactory" ref="sessionFactory"/> </bean> @Override @Transactional(propagation=Propagation.REQUIRED) public Reservation reserveShowing(int reserverId, int showingId, int audienceCount) { Customer reserver = customerRepository.find(reserverId); Showing showing = showingRepository.find(showingId); Reservation reservation = showing.reserve(reserver, audienceCount); reservationRepository.save(reservation); return reservation; }
  63. 63. 5. 결롞
  64. 64. 64 / 문서의 제목 Rich Domain Model  설계 관점이지 기술 관점이 아님  훌륭한 객체 지향 설계 지침을 따를 것
  65. 65. 65 / 문서의 제목 그러나 기술적인 제약 사항 역시 중요  비침투적인 프레임워크를 사용하라  프레임워크의 제약 사항을 파악하라  프레임워크의 제약 사항에 따라 구현 가능하도록 아키텍처를 수정하라 가지고 있는 도구 또한 아키텍처에 영향을 준다는 사실을 알 수 있다. 때로는 아키 텍처를 바탕으로 도구를 선택할 수 있으며, 이롞적으로는 그것이 올바른 방법이다. 그러나 실제로는 도구에 아키텍처를 맞추어야 한다. - Martin Fowler
  66. 66. 66 / 문서의 제목 Rich Domain Model을 자제해야 하는 경우 객체 지향 분석/설계 경험이 부족한 경우 비침투적인 프레임워크를 사용할 수 없는 경우 비침투적인 프레임워크에 대한 경험이 부족한 경우 O/R Mapper를 사용할 수 없는 경우 비즈니스 로직이 단순하고 개발 기간이 짧은 경우
  67. 67. 67 / 문서의 제목 첨언 Be Pragmatic
  68. 68. Thank you.
  69. 69. Question.
  70. 70. 70 / 문서의 제목 참고자료 - Patterns of Enterprise Application Architecture, Martin Fowler, Addison-Wesley, 2002 - Domain-Driven Design, Eric Evans, Addison-Wesley, 2003 - Expert One-on-One J2EE Development without EJB, Rod Johnson, Wrox, 2004 - Applying UML and Patterns 3rd Edition, Craig Larman, Prentice Hall, 2004 - Agile Software Development, Principles, Patterns, and Practices, Robert C. Martin, Prentice Hall, 2002 - Object Design : Roles, Responsibilities, and Collaborations , Rebecca Wirfs-Brock, Alan McKean, Addison-Wesley,2002 - POJOs in Action, Chris Richardson, Manning, 2006 - Java Persistence with Hibernate, Christian Bauer, Gavin King, Manning, 2006 - Spring in Action 2nd Edition, Craig Walls, Manning, 2007 - The New Holy Trinity, Ramnivas Laddad, http://www.aspectprogrammer.org/blogs/adrian/2005/03/the_new_holy_tr.html

×