瀏覽代碼

Made all domain model classes inherit common pattern base classes, removing lots of boilerplate code.

Made all immutable fields final, setting to null in package local no-args ctor (Hibernate will stil be able to set fields in runtime).

Added javadoc on Cargo.

Removed "empty itinerary" null pattern object, since a cargo is not always supposed to have an itinerary. More work left here though.
peter_backlund 17 年之前
父節點
當前提交
6cdbca4e47
共有 26 個文件被更改,包括 205 次插入502 次删除
  1. 40
    36
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Cargo.java
  2. 13
    35
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Delivery.java
  3. 4
    33
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Itinerary.java
  4. 11
    45
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Leg.java
  5. 6
    32
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Projections.java
  6. 7
    4
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/RouteSpecification.java
  7. 2
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/RoutingStatus.java
  8. 5
    23
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/TrackingId.java
  9. 1
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/TransportStatus.java
  10. 4
    21
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/EventSequenceNumber.java
  11. 1
    2
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingEvent.java
  12. 0
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingEventRepository.java
  13. 1
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingHistory.java
  14. 19
    17
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/CustomsZone.java
  15. 14
    39
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/Location.java
  16. 4
    23
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/UnLocode.java
  17. 10
    37
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/shared/HandlingActivity.java
  18. 8
    40
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/CarrierMovement.java
  19. 4
    24
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/Schedule.java
  20. 9
    28
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/Voyage.java
  21. 5
    25
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/VoyageNumber.java
  22. 8
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/shared/experimental/EntitySupport.java
  23. 0
    7
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/shared/experimental/ValueObject.java
  24. 3
    1
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/shared/experimental/ValueObjectSupport.java
  25. 26
    20
      dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/interfaces/booking/facade/DTOAssembler.java
  26. 0
    5
      dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/ItineraryTest.java

+ 40
- 36
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Cargo.java 查看文件

6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
7
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
8
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
8
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
9
-import se.citerus.dddsample.tracking.core.domain.shared.DomainObjectUtils;
10
-import se.citerus.dddsample.tracking.core.domain.shared.Entity;
9
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.EntitySupport;
11
 
10
 
12
 import java.util.Date;
11
 import java.util.Date;
13
 
12
 
46
  * or not a cargo is misdirected, what the current status of the cargo is (on board carrier,
45
  * or not a cargo is misdirected, what the current status of the cargo is (on board carrier,
47
  * in port etc), are captured in this aggregate.
46
  * in port etc), are captured in this aggregate.
48
  */
47
  */
49
-public class Cargo implements Entity<Cargo> {
48
+public class Cargo extends EntitySupport<Cargo,TrackingId> {
50
 
49
 
51
-  private TrackingId trackingId;
50
+  private final TrackingId trackingId;
52
   private RouteSpecification routeSpecification;
51
   private RouteSpecification routeSpecification;
53
   private Itinerary itinerary;
52
   private Itinerary itinerary;
54
   private Delivery delivery;
53
   private Delivery delivery;
64
     this.projections = new Projections(delivery, itinerary, routeSpecification);
63
     this.projections = new Projections(delivery, itinerary, routeSpecification);
65
   }
64
   }
66
 
65
 
66
+  @Override
67
+  public TrackingId identity() {
68
+    return trackingId;
69
+  }
70
+
67
   /**
71
   /**
68
    * The tracking id is the identity of this entity, and is unique.
72
    * The tracking id is the identity of this entity, and is unique.
69
    *
73
    *
77
    * @return The itinerary. Never null.
81
    * @return The itinerary. Never null.
78
    */
82
    */
79
   public Itinerary itinerary() {
83
   public Itinerary itinerary() {
80
-    return DomainObjectUtils.nullSafe(this.itinerary, Itinerary.EMPTY_ITINERARY);
84
+    return itinerary;
81
   }
85
   }
82
 
86
 
83
   /**
87
   /**
87
     return routeSpecification;
91
     return routeSpecification;
88
   }
92
   }
89
 
93
 
94
+  /**
95
+   * @return Estimated time of arrival.
96
+   */
90
   public Date estimatedTimeOfArrival() {
97
   public Date estimatedTimeOfArrival() {
91
     return projections.estimatedTimeOfArrival();
98
     return projections.estimatedTimeOfArrival();
92
   }
99
   }
93
 
100
 
101
+  /**
102
+   * @return Next expected activity.
103
+   */
94
   public HandlingActivity nextExpectedActivity() {
104
   public HandlingActivity nextExpectedActivity() {
95
     return projections.nextExpectedActivity();
105
     return projections.nextExpectedActivity();
96
   }
106
   }
97
 
107
 
108
+  /**
109
+   * @return True if cargo is misdirected.
110
+   */
98
   public boolean isMisdirected() {
111
   public boolean isMisdirected() {
99
     return delivery.isMisdirected(itinerary, routeSpecification);
112
     return delivery.isMisdirected(itinerary, routeSpecification);
100
   }
113
   }
101
 
114
 
115
+  /**
116
+   * @return Transport status.
117
+   */
102
   public TransportStatus transportStatus() {
118
   public TransportStatus transportStatus() {
103
     return delivery.transportStatus();
119
     return delivery.transportStatus();
104
   }
120
   }
105
 
121
 
122
+  /**
123
+   * @return Routing status.
124
+   */
106
   public RoutingStatus routingStatus() {
125
   public RoutingStatus routingStatus() {
107
     return delivery.routingStatus(itinerary, routeSpecification);
126
     return delivery.routingStatus(itinerary, routeSpecification);
108
   }
127
   }
109
 
128
 
129
+  /**
130
+   * @return Current voyage.
131
+   */
110
   public Voyage currentVoyage() {
132
   public Voyage currentVoyage() {
111
     return delivery.currentVoyage();
133
     return delivery.currentVoyage();
112
   }
134
   }
113
 
135
 
136
+  /**
137
+   * @return Last known location.
138
+   */
114
   public Location lastKnownLocation() {
139
   public Location lastKnownLocation() {
115
     return delivery.lastKnownLocation();
140
     return delivery.lastKnownLocation();
116
   }
141
   }
141
     this.projections = new Projections(delivery, itinerary, routeSpecification);
166
     this.projections = new Projections(delivery, itinerary, routeSpecification);
142
   }
167
   }
143
 
168
 
169
+  /**
170
+   * @return Customs zone.
171
+   */
144
   public CustomsZone customsZone() {
172
   public CustomsZone customsZone() {
145
     return routeSpecification.destination().customsZone();
173
     return routeSpecification.destination().customsZone();
146
   }
174
   }
147
 
175
 
148
 
176
 
177
+  /**
178
+   * @return Customs clearance point.
179
+   */
149
   public Location customsClearancePoint() {
180
   public Location customsClearancePoint() {
150
     return customsZone().entryPoint(itinerary.locations());
181
     return customsZone().entryPoint(itinerary.locations());
151
   }
182
   }
152
 
183
 
184
+  /**
185
+   * @return True if the cargo is ready to be claimed.
186
+   */
153
   public boolean isReadyToClaim() {
187
   public boolean isReadyToClaim() {
154
     return delivery.isUnloadedAtDestination(routeSpecification);
188
     return delivery.isUnloadedAtDestination(routeSpecification);
155
   }
189
   }
178
   }
212
   }
179
 
213
 
180
   @Override
214
   @Override
181
-  public boolean sameAs(final Cargo other) {
182
-    return other != null && trackingId().sameValueAs(other.trackingId());
183
-  }
184
-
185
-  /**
186
-   * @param object to compare
187
-   * @return True if they have the same identity
188
-   * @see #sameAs(Cargo)
189
-   */
190
-  @Override
191
-  public boolean equals(final Object object) {
192
-    if (this == object) return true;
193
-    if (object == null || getClass() != object.getClass()) return false;
194
-
195
-    final Cargo other = (Cargo) object;
196
-    return sameAs(other);
197
-  }
198
-
199
-  /**
200
-   * @return Hash code of tracking id.
201
-   */
202
-  @Override
203
-  public int hashCode() {
204
-    return trackingId.hashCode();
205
-  }
206
-
207
-  @Override
208
   public String toString() {
215
   public String toString() {
209
     return trackingId + " (" + routeSpecification + ")";
216
     return trackingId + " (" + routeSpecification + ")";
210
   }
217
   }
211
 
218
 
212
   Cargo() {
219
   Cargo() {
213
     // Needed by Hibernate
220
     // Needed by Hibernate
221
+    trackingId = null;
214
   }
222
   }
215
 
223
 
216
-  // Auto-generated surrogate key
217
-  @SuppressWarnings("UnusedDeclaration")
218
-  private Long id;
219
-
220
 }
224
 }

+ 13
- 35
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Delivery.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import org.apache.commons.lang.builder.EqualsBuilder;
5
-import org.apache.commons.lang.builder.HashCodeBuilder;
6
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.RoutingStatus.*;
4
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.RoutingStatus.*;
7
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.TransportStatus.ONBOARD_CARRIER;
5
 import static se.citerus.dddsample.tracking.core.domain.model.cargo.TransportStatus.ONBOARD_CARRIER;
8
 import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.*;
6
 import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.*;
9
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
10
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
8
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
11
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
9
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
12
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
10
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
13
 
11
 
14
 import java.util.Date;
12
 import java.util.Date;
15
 
13
 
17
  * Everything about the delivery of the cargo, i.e. where the cargo is
15
  * Everything about the delivery of the cargo, i.e. where the cargo is
18
  * right now, whether or not it's routed, misdirected and so on.
16
  * right now, whether or not it's routed, misdirected and so on.
19
  */
17
  */
20
-public class Delivery implements ValueObject<Delivery> {
18
+public class Delivery extends ValueObjectSupport<Delivery> {
21
 
19
 
22
-  private HandlingActivity mostRecentHandlingActivity;
23
-  private Date calculatedAt;
20
+  private final HandlingActivity mostRecentHandlingActivity;
21
+  private final Date calculatedAt;
24
 
22
 
25
   /**
23
   /**
26
    * Derives a new delivery when a cargo has been handled.
24
    * Derives a new delivery when a cargo has been handled.
57
    * @return Last known location of the cargo, or Location.UNKNOWN if the delivery history is empty.
55
    * @return Last known location of the cargo, or Location.UNKNOWN if the delivery history is empty.
58
    */
56
    */
59
   Location lastKnownLocation() {
57
   Location lastKnownLocation() {
60
-    if (mostRecentHandlingActivity != null) {
58
+    if (isHandled()) {
61
       return mostRecentHandlingActivity.location();
59
       return mostRecentHandlingActivity.location();
62
     } else {
60
     } else {
63
       return Location.UNKNOWN;
61
       return Location.UNKNOWN;
68
    * @return Current voyage.
66
    * @return Current voyage.
69
    */
67
    */
70
   Voyage currentVoyage() {
68
   Voyage currentVoyage() {
71
-    if (mostRecentHandlingActivity != null && transportStatus().equals(ONBOARD_CARRIER)) {
69
+    if (isHandled() && transportStatus().equals(ONBOARD_CARRIER)) {
72
       return mostRecentHandlingActivity.voyage();
70
       return mostRecentHandlingActivity.voyage();
73
     } else {
71
     } else {
74
       return Voyage.NONE;
72
       return Voyage.NONE;
75
     }
73
     }
76
   }
74
   }
77
 
75
 
76
+  private boolean isHandled() {
77
+    return mostRecentHandlingActivity != null;
78
+  }
79
+
78
   /**
80
   /**
79
    * Check if cargo is misdirected.
81
    * Check if cargo is misdirected.
80
    * <p/>
82
    * <p/>
105
    * @param routeSpecification route specification
107
    * @param routeSpecification route specification
106
    */
108
    */
107
   boolean isUnloadedAtDestination(final RouteSpecification routeSpecification) {
109
   boolean isUnloadedAtDestination(final RouteSpecification routeSpecification) {
108
-    return mostRecentHandlingActivity != null &&
110
+    return isHandled() &&
109
           (CLAIM.sameValueAs(mostRecentHandlingActivity.type()) || UNLOAD.sameValueAs(mostRecentHandlingActivity.type()) &&
111
           (CLAIM.sameValueAs(mostRecentHandlingActivity.type()) || UNLOAD.sameValueAs(mostRecentHandlingActivity.type()) &&
110
            routeSpecification.destination().sameAs(mostRecentHandlingActivity.location()));
112
            routeSpecification.destination().sameAs(mostRecentHandlingActivity.location()));
111
   }
113
   }
144
            !isMisdirected(itinerary, routeSpecification);
146
            !isMisdirected(itinerary, routeSpecification);
145
   }
147
   }
146
 
148
 
147
-  @Override
148
-  public boolean sameValueAs(final Delivery other) {
149
-    return other != null && new EqualsBuilder().
150
-      append(this.mostRecentHandlingActivity, other.mostRecentHandlingActivity).
151
-      append(this.calculatedAt, other.calculatedAt).
152
-      isEquals();
153
-  }
154
-
155
-  @Override
156
-  public boolean equals(final Object o) {
157
-    if (this == o) return true;
158
-    if (o == null || getClass() != o.getClass()) return false;
159
-
160
-    final Delivery other = (Delivery) o;
161
-
162
-    return sameValueAs(other);
163
-  }
164
-
165
-  @Override
166
-  public int hashCode() {
167
-    return new HashCodeBuilder().
168
-      append(mostRecentHandlingActivity).
169
-      append(calculatedAt).
170
-      toHashCode();
171
-  }
172
-
173
   Delivery() {
149
   Delivery() {
174
     // Needed by Hibernate
150
     // Needed by Hibernate
151
+    calculatedAt = null;
152
+    mostRecentHandlingActivity = null;
175
   }
153
   }
176
 
154
 
177
 }
155
 }

+ 4
- 33
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Itinerary.java 查看文件

6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
7
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
8
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
8
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
9
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
9
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
10
 
10
 
11
 import java.util.*;
11
 import java.util.*;
12
 
12
 
13
 /**
13
 /**
14
  * An itinerary.
14
  * An itinerary.
15
  */
15
  */
16
-public class Itinerary implements ValueObject<Itinerary> {
16
+public class Itinerary extends ValueObjectSupport<Itinerary> {
17
 
17
 
18
-  private List<Leg> legs = Collections.emptyList();
19
-
20
-  static final Itinerary EMPTY_ITINERARY = new Itinerary();
18
+  private final List<Leg> legs;
21
   private static final Date END_OF_DAYS = new Date(Long.MAX_VALUE);
19
   private static final Date END_OF_DAYS = new Date(Long.MAX_VALUE);
22
 
20
 
23
   /**
21
   /**
210
     return null;
208
     return null;
211
   }
209
   }
212
 
210
 
213
-
214
-  /**
215
-   * @param other itinerary to compare
216
-   * @return <code>true</code> if the legs in this and the other itinerary are all equal.
217
-   */
218
-  @Override
219
-  public boolean sameValueAs(final Itinerary other) {
220
-    return other != null && legs.equals(other.legs);
221
-  }
222
-
223
-  @Override
224
-  public boolean equals(final Object o) {
225
-    if (this == o) return true;
226
-    if (o == null || getClass() != o.getClass()) return false;
227
-
228
-    final Itinerary itinerary = (Itinerary) o;
229
-
230
-    return sameValueAs(itinerary);
231
-  }
232
-
233
-  @Override
234
-  public int hashCode() {
235
-    return legs.hashCode();
236
-  }
237
-
238
   @Override
211
   @Override
239
   public String toString() {
212
   public String toString() {
240
     return StringUtils.join(legs, "\n");
213
     return StringUtils.join(legs, "\n");
242
 
215
 
243
   Itinerary() {
216
   Itinerary() {
244
     // Needed by Hibernate
217
     // Needed by Hibernate
218
+    legs = null;
245
   }
219
   }
246
 
220
 
247
-  // Auto-generated surrogate key
248
-  @SuppressWarnings("UnusedDeclaration")
249
-  private Long id;
250
 }
221
 }

+ 11
- 45
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Leg.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import org.apache.commons.lang.builder.EqualsBuilder;
5
-import org.apache.commons.lang.builder.HashCodeBuilder;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
4
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
5
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
8
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
6
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
9
 
7
 
10
 import java.util.Date;
8
 import java.util.Date;
11
 
9
 
12
 /**
10
 /**
13
  * An itinerary consists of one or more legs.
11
  * An itinerary consists of one or more legs.
14
  */
12
  */
15
-public class Leg implements ValueObject<Leg> {
13
+public class Leg extends ValueObjectSupport<Leg> {
16
 
14
 
17
-  private Voyage voyage;
18
-  private Location loadLocation;
19
-  private Location unloadLocation;
20
-  private Date loadTime;
21
-  private Date unloadTime;
15
+  private final Voyage voyage;
16
+  private final Location loadLocation;
17
+  private final Location unloadLocation;
18
+  private final Date loadTime;
19
+  private final Date unloadTime;
22
 
20
 
23
-  // TODO hide this, use factory
21
+  // TODO hide this, use factory only
24
   public Leg(Voyage voyage, Location loadLocation, Location unloadLocation, Date loadTime, Date unloadTime) {
22
   public Leg(Voyage voyage, Location loadLocation, Location unloadLocation, Date loadTime, Date unloadTime) {
25
     Validate.notNull(voyage, "Voyage is required");
23
     Validate.notNull(voyage, "Voyage is required");
26
     Validate.notNull(loadLocation, "Load location is required");
24
     Validate.notNull(loadLocation, "Load location is required");
86
   }
84
   }
87
 
85
 
88
   @Override
86
   @Override
89
-  public boolean sameValueAs(final Leg other) {
90
-    return other != null && new EqualsBuilder().
91
-      append(this.voyage, other.voyage).
92
-      append(this.loadLocation, other.loadLocation).
93
-      append(this.unloadLocation, other.unloadLocation).
94
-      append(this.loadTime, other.loadTime).
95
-      append(this.unloadTime, other.unloadTime).
96
-      isEquals();
97
-  }
98
-
99
-  @Override
100
-  public boolean equals(final Object o) {
101
-    if (this == o) return true;
102
-    if (o == null || getClass() != o.getClass()) return false;
103
-
104
-    Leg leg = (Leg) o;
105
-
106
-    return sameValueAs(leg);
107
-  }
108
-
109
-  @Override
110
-  public int hashCode() {
111
-    return new HashCodeBuilder().
112
-      append(voyage).
113
-      append(loadLocation).
114
-      append(unloadLocation).
115
-      append(loadTime).
116
-      append(unloadTime).
117
-      toHashCode();
118
-  }
119
-
120
-  @Override
121
   public String toString() {
87
   public String toString() {
122
     return "Load in " + loadLocation + " at " + loadTime +
88
     return "Load in " + loadLocation + " at " + loadTime +
123
       " --- Unload in " + unloadLocation + " at " + unloadTime;
89
       " --- Unload in " + unloadLocation + " at " + unloadTime;
125
 
91
 
126
   Leg() {
92
   Leg() {
127
     // Needed by Hibernate
93
     // Needed by Hibernate
94
+    voyage = null;
95
+    loadLocation = unloadLocation = null;
96
+    loadTime = unloadTime = null;
128
   }
97
   }
129
 
98
 
130
-  // Auto-generated surrogate key
131
-  private Long id;
132
-
133
 }
99
 }

+ 6
- 32
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/Projections.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
2
 
2
 
3
-import org.apache.commons.lang.builder.EqualsBuilder;
4
-import org.apache.commons.lang.builder.HashCodeBuilder;
5
 import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.*;
3
 import static se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent.Type.*;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
4
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
5
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
8
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
6
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
9
 
7
 
10
 import java.util.Date;
8
 import java.util.Date;
11
 import java.util.Iterator;
9
 import java.util.Iterator;
16
  * <p/>
14
  * <p/>
17
  * It is updated on routing changes as well as handling.
15
  * It is updated on routing changes as well as handling.
18
  */
16
  */
19
-class Projections implements ValueObject<Projections> {
17
+class Projections extends ValueObjectSupport<Projections> {
20
 
18
 
21
-  private Date estimatedTimeOfArrival;
22
-  private HandlingActivity nextExpectedActivity;
19
+  private final Date estimatedTimeOfArrival;
20
+  private final HandlingActivity nextExpectedActivity;
23
 
21
 
24
   private static final Date ETA_UNKOWN = null;
22
   private static final Date ETA_UNKOWN = null;
25
   private static final HandlingActivity NO_ACTIVITY = null;
23
   private static final HandlingActivity NO_ACTIVITY = null;
134
     return NO_ACTIVITY;
132
     return NO_ACTIVITY;
135
   }
133
   }
136
 
134
 
137
-
138
-  @Override
139
-  public boolean sameValueAs(final Projections other) {
140
-    return other != null && new EqualsBuilder().
141
-      append(this.estimatedTimeOfArrival, other.estimatedTimeOfArrival).
142
-      append(this.nextExpectedActivity, other.nextExpectedActivity).
143
-      isEquals();
144
-  }
145
-
146
-  @Override
147
-  public boolean equals(final Object o) {
148
-    if (this == o) return true;
149
-    if (o == null || getClass() != o.getClass()) return false;
150
-
151
-    final Projections other = (Projections) o;
152
-    return sameValueAs(other);
153
-  }
154
-
155
-  @Override
156
-  public int hashCode() {
157
-    return new HashCodeBuilder().
158
-      append(estimatedTimeOfArrival).
159
-      append(nextExpectedActivity).
160
-      toHashCode();
161
-  }
162
-
163
   Projections() {
135
   Projections() {
164
     // Needed by Hibernate
136
     // Needed by Hibernate
137
+    estimatedTimeOfArrival = null;
138
+    nextExpectedActivity = null;
165
   }
139
   }
166
 
140
 
167
 }
141
 }

+ 7
- 4
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/RouteSpecification.java 查看文件

5
 import org.apache.commons.lang.builder.HashCodeBuilder;
5
 import org.apache.commons.lang.builder.HashCodeBuilder;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.shared.AbstractSpecification;
7
 import se.citerus.dddsample.tracking.core.domain.shared.AbstractSpecification;
8
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
8
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObject;
9
 
9
 
10
 import java.util.Date;
10
 import java.util.Date;
11
 
11
 
13
  * Route specification. Describes where a cargo orign and destination is,
13
  * Route specification. Describes where a cargo orign and destination is,
14
  * and the arrival deadline.
14
  * and the arrival deadline.
15
  */
15
  */
16
+// TODO use composition instead of inheritance
16
 public class RouteSpecification extends AbstractSpecification<Itinerary> implements ValueObject<RouteSpecification> {
17
 public class RouteSpecification extends AbstractSpecification<Itinerary> implements ValueObject<RouteSpecification> {
17
 
18
 
18
-  private Location origin;
19
-  private Location destination;
20
-  private Date arrivalDeadline;
19
+  private final Location origin;
20
+  private final Location destination;
21
+  private final Date arrivalDeadline;
21
 
22
 
22
   /**
23
   /**
23
    * @param origin          origin location - can't be the same as the destination
24
    * @param origin          origin location - can't be the same as the destination
123
 
124
 
124
   RouteSpecification() {
125
   RouteSpecification() {
125
     // Needed by Hibernate
126
     // Needed by Hibernate
127
+    origin = destination = null;
128
+    arrivalDeadline = null;
126
   }
129
   }
127
 
130
 
128
 }
131
 }

+ 2
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/RoutingStatus.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
2
 
2
 
3
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
3
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObject;
4
+
4
 
5
 
5
 /**
6
 /**
6
  * The different routing statuses of a cargo.
7
  * The different routing statuses of a cargo.

+ 5
- 23
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/TrackingId.java 查看文件

2
 
2
 
3
 import org.apache.commons.lang.StringUtils;
3
 import org.apache.commons.lang.StringUtils;
4
 import org.apache.commons.lang.Validate;
4
 import org.apache.commons.lang.Validate;
5
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
5
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
6
 
6
 
7
 import java.io.Serializable;
7
 import java.io.Serializable;
8
 
8
 
9
 /**
9
 /**
10
  * Uniquely identifies a particular cargo. Automatically generated by the application.
10
  * Uniquely identifies a particular cargo. Automatically generated by the application.
11
  */
11
  */
12
-public final class TrackingId implements ValueObject<TrackingId>, Serializable {
12
+public final class TrackingId extends ValueObjectSupport<TrackingId> implements Serializable {
13
 
13
 
14
-  private String id;
14
+  private final String id;
15
 
15
 
16
   /**
16
   /**
17
    * Constructor.
17
    * Constructor.
36
   }
36
   }
37
 
37
 
38
   @Override
38
   @Override
39
-  public boolean equals(Object o) {
40
-    if (this == o) return true;
41
-    if (o == null || getClass() != o.getClass()) return false;
42
-
43
-    TrackingId other = (TrackingId) o;
44
-
45
-    return sameValueAs(other);
46
-  }
47
-
48
-  @Override
49
-  public int hashCode() {
50
-    return id.hashCode();
51
-  }
52
-
53
-  @Override
54
-  public boolean sameValueAs(TrackingId other) {
55
-    return other != null && this.id.equals(other.id);
56
-  }
57
-
58
-  @Override
59
   public String toString() {
39
   public String toString() {
60
     return id;
40
     return id;
61
   }
41
   }
62
 
42
 
63
   TrackingId() {
43
   TrackingId() {
64
     // Needed by Hibernate
44
     // Needed by Hibernate
45
+    id = null;
65
   }
46
   }
47
+  
66
 }
48
 }

+ 1
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/cargo/TransportStatus.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
1
 package se.citerus.dddsample.tracking.core.domain.model.cargo;
2
 
2
 
3
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
3
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
4
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
4
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObject;
5
 
5
 
6
 /**
6
 /**
7
  * Represents the different transport statuses for a cargo.
7
  * Represents the different transport statuses for a cargo.

+ 4
- 21
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/EventSequenceNumber.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.handling;
1
 package se.citerus.dddsample.tracking.core.domain.model.handling;
2
 
2
 
3
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
3
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
4
 
4
 
5
 import java.util.concurrent.atomic.AtomicLong;
5
 import java.util.concurrent.atomic.AtomicLong;
6
 
6
 
7
-public class EventSequenceNumber implements ValueObject<EventSequenceNumber> {
7
+public class EventSequenceNumber extends ValueObjectSupport<EventSequenceNumber> {
8
 
8
 
9
-  private long value;
9
+  private final long value;
10
   private static final AtomicLong SEQUENCE = new AtomicLong(System.currentTimeMillis());
10
   private static final AtomicLong SEQUENCE = new AtomicLong(System.currentTimeMillis());
11
 
11
 
12
   private EventSequenceNumber(final long value) {
12
   private EventSequenceNumber(final long value) {
22
   }
22
   }
23
 
23
 
24
   @Override
24
   @Override
25
-  public boolean sameValueAs(final EventSequenceNumber other) {
26
-    return false;
27
-  }
28
-
29
-  @Override
30
-  public boolean equals(Object o) {
31
-    if (this == o) return true;
32
-    if (o == null || getClass() != o.getClass()) return false;
33
-
34
-    return sameValueAs((EventSequenceNumber) o);
35
-  }
36
-
37
-  @Override
38
-  public int hashCode() {
39
-    return Long.valueOf(value).hashCode();
40
-  }
41
-
42
-  @Override
43
   public String toString() {
25
   public String toString() {
44
     return String.valueOf(value);
26
     return String.valueOf(value);
45
   }
27
   }
46
 
28
 
47
   EventSequenceNumber() {
29
   EventSequenceNumber() {
48
     // Needed by Hibernate
30
     // Needed by Hibernate
31
+    value = -1L;
49
   }
32
   }
50
 
33
 
51
 }
34
 }

+ 1
- 2
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingEvent.java 查看文件

5
 import org.apache.commons.lang.builder.HashCodeBuilder;
5
 import org.apache.commons.lang.builder.HashCodeBuilder;
6
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
6
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
7
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
8
-import se.citerus.dddsample.tracking.core.domain.model.handling.EventSequenceNumber;
9
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
8
 import se.citerus.dddsample.tracking.core.domain.model.shared.HandlingActivity;
10
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
9
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
11
 import se.citerus.dddsample.tracking.core.domain.shared.DomainEvent;
10
 import se.citerus.dddsample.tracking.core.domain.shared.DomainEvent;
12
 import se.citerus.dddsample.tracking.core.domain.shared.DomainObjectUtils;
11
 import se.citerus.dddsample.tracking.core.domain.shared.DomainObjectUtils;
13
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
12
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObject;
14
 
13
 
15
 import java.util.Date;
14
 import java.util.Date;
16
 
15
 

+ 0
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingEventRepository.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.handling;
1
 package se.citerus.dddsample.tracking.core.domain.model.handling;
2
 
2
 
3
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
3
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
4
-import se.citerus.dddsample.tracking.core.domain.model.handling.EventSequenceNumber;
5
 
4
 
6
 /**
5
 /**
7
  * Handling event repository.
6
  * Handling event repository.

+ 1
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/handling/HandlingHistory.java 查看文件

2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
4
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
5
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
5
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObject;
6
 
6
 
7
 import java.util.*;
7
 import java.util.*;
8
 import static java.util.Collections.sort;
8
 import static java.util.Collections.sort;

+ 19
- 17
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/CustomsZone.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.location;
1
 package se.citerus.dddsample.tracking.core.domain.model.location;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import se.citerus.dddsample.tracking.core.domain.shared.Entity;
4
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.EntitySupport;
5
 
5
 
6
 import java.util.Arrays;
6
 import java.util.Arrays;
7
 import java.util.List;
7
 import java.util.List;
10
 /**
10
 /**
11
  * A geographical zone within which there are no customs restrictions or checks.
11
  * A geographical zone within which there are no customs restrictions or checks.
12
  */
12
  */
13
-public class CustomsZone implements Entity<CustomsZone> {
13
+public class CustomsZone extends EntitySupport<CustomsZone,String> {
14
 
14
 
15
-  private String code;
16
-  private String name;
15
+  private final String code;
16
+  private final String name;
17
 
17
 
18
   // TODO: Find out what the standards are for this, if any. For now:
18
   // TODO: Find out what the standards are for this, if any. For now:
19
   // For CustomsZone code, we are using the "country code" portion of the UnLocode,
19
   // For CustomsZone code, we are using the "country code" portion of the UnLocode,
36
     this.name = name;
36
     this.name = name;
37
   }
37
   }
38
 
38
 
39
+  @Override
40
+  public String identity() {
41
+    return code;
42
+  }
43
+
39
   /**
44
   /**
40
    * Where would a Cargo enter this CustomsZone if it were
45
    * Where would a Cargo enter this CustomsZone if it were
41
    * following this route. Note that specific voyages, etc do not
46
    * following this route. Note that specific voyages, etc do not
44
    * @param route
49
    * @param route
45
    * @return
50
    * @return
46
    */
51
    */
47
-  public Location entryPoint(List<Location> route) {
52
+  public Location entryPoint(final List<Location> route) {
48
     for (Location location : route) {
53
     for (Location location : route) {
49
-      if (this.includes(location)) return location;
54
+      if (this.includes(location)) {
55
+        return location;
56
+      }
50
     }
57
     }
51
     return null; //The route does not enter this CustomsZone
58
     return null; //The route does not enter this CustomsZone
52
   }
59
   }
58
    * @param route
65
    * @param route
59
    * @return
66
    * @return
60
    */
67
    */
61
-  public Location entryPoint(Location... route) {
68
+  public Location entryPoint(final Location... route) {
62
     return entryPoint(Arrays.asList(route));
69
     return entryPoint(Arrays.asList(route));
63
   }
70
   }
64
 
71
 
102
     return name;
109
     return name;
103
   }
110
   }
104
 
111
 
105
-  @Override
106
-  public String toString() {
107
-    return code + "[" + name + "]";
112
+  public boolean includes(final Location location) {
113
+    return this.sameAs(location.customsZone());
108
   }
114
   }
109
 
115
 
110
   @Override
116
   @Override
111
-  public boolean sameAs(CustomsZone other) {
112
-    return code.equals(other.code);
117
+  public String toString() {
118
+    return code + "[" + name + "]";
113
   }
119
   }
114
 
120
 
115
   CustomsZone() {
121
   CustomsZone() {
116
     // Needed by Hibernate
122
     // Needed by Hibernate
123
+    code = name = null;
117
   }
124
   }
118
 
125
 
119
-  public boolean includes(Location location) {
120
-    return this.sameAs(location.customsZone());
121
-  }
122
-
123
-
124
 }
126
 }

+ 14
- 39
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/Location.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.location;
1
 package se.citerus.dddsample.tracking.core.domain.model.location;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import se.citerus.dddsample.tracking.core.domain.shared.Entity;
4
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.EntitySupport;
5
 
5
 
6
 import java.util.TimeZone;
6
 import java.util.TimeZone;
7
 
7
 
11
  * <p/>
11
  * <p/>
12
  * It is uniquely identified by a UN Locode.
12
  * It is uniquely identified by a UN Locode.
13
  */
13
  */
14
-public final class Location implements Entity<Location> {
14
+public final class Location extends EntitySupport<Location, UnLocode> {
15
 
15
 
16
-  private UnLocode unLocode;
16
+  private final UnLocode unLocode;
17
   private String name;
17
   private String name;
18
   private TimeZone timeZone;
18
   private TimeZone timeZone;
19
   private CustomsZone customsZone;
19
   private CustomsZone customsZone;
30
    *
30
    *
31
    * @param unLocode    UN Locode
31
    * @param unLocode    UN Locode
32
    * @param name        location name
32
    * @param name        location name
33
-   * @param timeZone
34
-   * @param customsZone
33
+   * @param timeZone    time zone
34
+   * @param customsZone customs zone
35
    * @throws IllegalArgumentException if the UN Locode or name is null
35
    * @throws IllegalArgumentException if the UN Locode or name is null
36
    */
36
    */
37
   Location(final UnLocode unLocode, final String name, final TimeZone timeZone, final CustomsZone customsZone) {
37
   Location(final UnLocode unLocode, final String name, final TimeZone timeZone, final CustomsZone customsZone) {
46
     this.customsZone = customsZone;
46
     this.customsZone = customsZone;
47
   }
47
   }
48
 
48
 
49
+  @Override
50
+  public UnLocode identity() {
51
+    return unLocode;
52
+  }
53
+
49
   /**
54
   /**
50
    * @return UN Locode for this location.
55
    * @return UN Locode for this location.
51
    */
56
    */
68
   }
73
   }
69
 
74
 
70
   /**
75
   /**
71
-   * @param object to compare
72
-   * @return Since this is an entity this will be true iff UN locodes are equal.
73
-   */
74
-  @Override
75
-  public boolean equals(final Object object) {
76
-    if (object == null) {
77
-      return false;
78
-    }
79
-    if (this == object) {
80
-      return true;
81
-    }
82
-    if (!(object instanceof Location)) {
83
-      return false;
84
-    }
85
-    Location other = (Location) object;
86
-    return sameAs(other);
87
-  }
88
-
89
-  @Override
90
-  public boolean sameAs(final Location other) {
91
-    return this.unLocode.sameValueAs(other.unLocode);
92
-  }
93
-
94
-  /**
95
-   * @return Hash code of UN locode.
76
+   * @return Time zone of this location.
96
    */
77
    */
97
-  @Override
98
-  public int hashCode() {
99
-    return unLocode.hashCode();
78
+  public TimeZone timeZone() {
79
+    return timeZone;
100
   }
80
   }
101
 
81
 
102
   @Override
82
   @Override
106
 
86
 
107
   Location() {
87
   Location() {
108
     // Needed by Hibernate
88
     // Needed by Hibernate
109
-  }
110
-
111
-  private Long id;
112
-
113
-  public TimeZone timeZone() {
114
-    return timeZone;
89
+    unLocode = null;
115
   }
90
   }
116
 
91
 
117
 }
92
 }

+ 4
- 23
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/location/UnLocode.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.location;
1
 package se.citerus.dddsample.tracking.core.domain.model.location;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
4
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
5
 
5
 
6
 import java.util.regex.Pattern;
6
 import java.util.regex.Pattern;
7
 
7
 
11
  * http://www.unece.org/cefact/locode/
11
  * http://www.unece.org/cefact/locode/
12
  * http://www.unece.org/cefact/locode/DocColumnDescription.htm#LOCODE
12
  * http://www.unece.org/cefact/locode/DocColumnDescription.htm#LOCODE
13
  */
13
  */
14
-public final class UnLocode implements ValueObject<UnLocode> {
14
+public final class UnLocode extends ValueObjectSupport<UnLocode> {
15
 
15
 
16
-  private String unlocode;
16
+  private final String unlocode;
17
 
17
 
18
   // Country code is exactly two letters.
18
   // Country code is exactly two letters.
19
   // Location code is usually three letters, but may contain the numbers 2-9 as well
19
   // Location code is usually three letters, but may contain the numbers 2-9 as well
40
   }
40
   }
41
 
41
 
42
   @Override
42
   @Override
43
-  public boolean equals(final Object o) {
44
-    if (this == o) return true;
45
-    if (o == null || getClass() != o.getClass()) return false;
46
-
47
-    UnLocode other = (UnLocode) o;
48
-
49
-    return sameValueAs(other);
50
-  }
51
-
52
-  @Override
53
-  public int hashCode() {
54
-    return unlocode.hashCode();
55
-  }
56
-
57
-  @Override
58
-  public boolean sameValueAs(UnLocode other) {
59
-    return other != null && this.unlocode.equals(other.unlocode);
60
-  }
61
-
62
-  @Override
63
   public String toString() {
43
   public String toString() {
64
     return stringValue();
44
     return stringValue();
65
   }
45
   }
66
 
46
 
67
   UnLocode() {
47
   UnLocode() {
68
     // Needed by Hibernate
48
     // Needed by Hibernate
49
+    unlocode = null;
69
   }
50
   }
70
 
51
 
71
 }
52
 }

+ 10
- 37
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/shared/HandlingActivity.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.shared;
1
 package se.citerus.dddsample.tracking.core.domain.model.shared;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import org.apache.commons.lang.builder.EqualsBuilder;
5
-import org.apache.commons.lang.builder.HashCodeBuilder;
6
 import se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent;
4
 import se.citerus.dddsample.tracking.core.domain.model.handling.HandlingEvent;
7
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
5
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
8
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
6
 import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
9
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
7
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
10
 
8
 
11
 /**
9
 /**
12
  * A handling activity represents how and where a cargo can be handled,
10
  * A handling activity represents how and where a cargo can be handled,
13
  * and can be used to express predictions about what is expected to
11
  * and can be used to express predictions about what is expected to
14
  * happen to a cargo in the future.
12
  * happen to a cargo in the future.
15
  */
13
  */
16
-public class HandlingActivity implements ValueObject<HandlingActivity> {
14
+public class HandlingActivity extends ValueObjectSupport<HandlingActivity> {
17
 
15
 
18
   // TODO introduce something like this (?):
16
   // TODO introduce something like this (?):
19
   // HandlingActivity.loadOnto(voyage).in(location)
17
   // HandlingActivity.loadOnto(voyage).in(location)
20
   // HandlingActivity.claimIn(location)
18
   // HandlingActivity.claimIn(location)
21
 
19
 
22
-  private HandlingEvent.Type type;
23
-  private Location location;
24
-  private Voyage voyage;
20
+  private final HandlingEvent.Type type;
21
+  private final Location location;
22
+  private final Voyage voyage;
25
 
23
 
26
   public HandlingActivity(final HandlingEvent.Type type, final Location location) {
24
   public HandlingActivity(final HandlingEvent.Type type, final Location location) {
27
     Validate.notNull(type, "Handling event type is required");
25
     Validate.notNull(type, "Handling event type is required");
29
 
27
 
30
     this.type = type;
28
     this.type = type;
31
     this.location = location;
29
     this.location = location;
30
+    this.voyage = null;
32
   }
31
   }
33
 
32
 
34
   public HandlingActivity(final HandlingEvent.Type type, final Location location, final Voyage voyage) {
33
   public HandlingActivity(final HandlingEvent.Type type, final Location location, final Voyage voyage) {
35
     Validate.notNull(type, "Handling event type is required");
34
     Validate.notNull(type, "Handling event type is required");
36
     Validate.notNull(location, "Location is required");
35
     Validate.notNull(location, "Location is required");
37
-    Validate.notNull(location, "Voyage is required");
36
+    Validate.notNull(voyage, "Voyage is required");
38
 
37
 
39
     this.type = type;
38
     this.type = type;
40
     this.location = location;
39
     this.location = location;
54
   }
53
   }
55
 
54
 
56
   @Override
55
   @Override
57
-  public boolean sameValueAs(final HandlingActivity other) {
58
-    return other != null && new EqualsBuilder().
59
-      append(this.type, other.type).
60
-      append(this.location, other.location).
61
-      append(this.voyage, other.voyage).
62
-      isEquals();
63
-  }
64
-
65
-  @Override
66
-  public int hashCode() {
67
-    return new HashCodeBuilder().
68
-      append(this.type).
69
-      append(this.location).
70
-      append(this.voyage).
71
-      toHashCode();
72
-  }
73
-
74
-  @Override
75
-  public boolean equals(final Object obj) {
76
-    if (obj == this) return true;
77
-    if (obj == null) return false;
78
-    if (obj.getClass() != this.getClass()) return false;
79
-
80
-    HandlingActivity other = (HandlingActivity) obj;
81
-
82
-    return sameValueAs(other);
83
-  }
84
-
85
-  @Override
86
   public String toString() {
56
   public String toString() {
87
     return type + " in " + location + (voyage != null ? ", " + voyage : "");
57
     return type + " in " + location + (voyage != null ? ", " + voyage : "");
88
   }
58
   }
89
 
59
 
90
   HandlingActivity() {
60
   HandlingActivity() {
91
     // Needed by Hibernate
61
     // Needed by Hibernate
62
+    type = null;
63
+    location = null;
64
+    voyage = null;
92
   }
65
   }
93
 
66
 
94
 }
67
 }

+ 8
- 40
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/CarrierMovement.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.voyage;
1
 package se.citerus.dddsample.tracking.core.domain.model.voyage;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import org.apache.commons.lang.builder.EqualsBuilder;
5
-import org.apache.commons.lang.builder.HashCodeBuilder;
6
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
4
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
7
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
5
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
8
 
6
 
9
 import java.util.Date;
7
 import java.util.Date;
10
 
8
 
12
 /**
10
 /**
13
  * A carrier movement is a vessel voyage from one location to another.
11
  * A carrier movement is a vessel voyage from one location to another.
14
  */
12
  */
15
-public final class CarrierMovement implements ValueObject<CarrierMovement> {
13
+public final class CarrierMovement extends ValueObjectSupport<CarrierMovement> {
16
 
14
 
17
-  private Location departureLocation;
18
-  private Location arrivalLocation;
19
-  private Date departureTime;
20
-  private Date arrivalTime;
15
+  private final Location departureLocation;
16
+  private final Location arrivalLocation;
17
+  private final Date departureTime;
18
+  private final Date arrivalTime;
21
 
19
 
22
   // Null object pattern 
20
   // Null object pattern 
23
   public static final CarrierMovement NONE = new CarrierMovement(
21
   public static final CarrierMovement NONE = new CarrierMovement(
86
     );
84
     );
87
   }
85
   }
88
 
86
 
89
-  @Override
90
-  public boolean equals(final Object o) {
91
-    if (this == o) return true;
92
-    if (o == null || getClass() != o.getClass()) return false;
93
-
94
-    final CarrierMovement that = (CarrierMovement) o;
95
-
96
-    return sameValueAs(that);
97
-  }
98
-
99
-  @Override
100
-  public int hashCode() {
101
-    return new HashCodeBuilder().
102
-      append(this.departureLocation).
103
-      append(this.departureTime).
104
-      append(this.arrivalLocation).
105
-      append(this.arrivalTime).
106
-      toHashCode();
107
-  }
108
-
109
-  @Override
110
-  public boolean sameValueAs(CarrierMovement other) {
111
-    return other != null && new EqualsBuilder().
112
-      append(this.departureLocation, other.departureLocation).
113
-      append(this.departureTime, other.departureTime).
114
-      append(this.arrivalLocation, other.arrivalLocation).
115
-      append(this.arrivalTime, other.arrivalTime).
116
-      isEquals();
117
-  }
118
-
119
   CarrierMovement() {
87
   CarrierMovement() {
120
     // Needed by Hibernate
88
     // Needed by Hibernate
89
+    arrivalLocation = departureLocation = null;
90
+    arrivalTime = departureTime = null;
121
   }
91
   }
122
 
92
 
123
-  // Auto-generated surrogate key
124
-  private Long id;
125
 }
93
 }

+ 4
- 24
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/Schedule.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.voyage;
1
 package se.citerus.dddsample.tracking.core.domain.model.voyage;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import org.apache.commons.lang.builder.HashCodeBuilder;
5
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
4
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
6
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
5
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
7
 
6
 
8
 import java.util.Collections;
7
 import java.util.Collections;
9
 import java.util.Date;
8
 import java.util.Date;
12
 /**
11
 /**
13
  * A voyage schedule.
12
  * A voyage schedule.
14
  */
13
  */
15
-public class Schedule implements ValueObject<Schedule> {
14
+public class Schedule extends ValueObjectSupport<Schedule> {
16
 
15
 
17
-  private List<CarrierMovement> carrierMovements;
16
+  private final List<CarrierMovement> carrierMovements;
18
 
17
 
19
   public static final Schedule EMPTY = new Schedule();
18
   public static final Schedule EMPTY = new Schedule();
20
 
19
 
59
     return null;
58
     return null;
60
   }
59
   }
61
 
60
 
62
-  @Override
63
-  public boolean sameValueAs(final Schedule other) {
64
-    return other != null && this.carrierMovements.equals(other.carrierMovements);
65
-  }
66
-
67
-  @Override
68
-  public boolean equals(final Object o) {
69
-    if (this == o) return true;
70
-    if (o == null || getClass() != o.getClass()) return false;
71
-
72
-    final Schedule that = (Schedule) o;
73
-
74
-    return sameValueAs(that);
75
-  }
76
-
77
-  @Override
78
-  public int hashCode() {
79
-    return new HashCodeBuilder().append(this.carrierMovements).toHashCode();
80
-  }
81
-
82
   Schedule() {
61
   Schedule() {
83
     // Needed by Hibernate
62
     // Needed by Hibernate
63
+    carrierMovements = null;
84
   }
64
   }
85
 }
65
 }

+ 9
- 28
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/Voyage.java 查看文件

2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
4
 import se.citerus.dddsample.tracking.core.domain.model.location.Location;
5
-import se.citerus.dddsample.tracking.core.domain.shared.Entity;
5
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.EntitySupport;
6
 
6
 
7
 import java.util.ArrayList;
7
 import java.util.ArrayList;
8
 import java.util.Date;
8
 import java.util.Date;
11
 /**
11
 /**
12
  * A Voyage.
12
  * A Voyage.
13
  */
13
  */
14
-public class Voyage implements Entity<Voyage> {
14
+public class Voyage extends EntitySupport<Voyage,VoyageNumber> {
15
 
15
 
16
-  private VoyageNumber voyageNumber;
16
+  private final VoyageNumber voyageNumber;
17
   private Schedule schedule;
17
   private Schedule schedule;
18
 
18
 
19
   // Null object pattern
19
   // Null object pattern
29
     this.schedule = schedule;
29
     this.schedule = schedule;
30
   }
30
   }
31
 
31
 
32
+  @Override
33
+  public VoyageNumber identity() {
34
+    return voyageNumber;
35
+  }
36
+  
32
   /**
37
   /**
33
    * @return Voyage number.
38
    * @return Voyage number.
34
    */
39
    */
65
   }
70
   }
66
 
71
 
67
   @Override
72
   @Override
68
-  public int hashCode() {
69
-    return voyageNumber.hashCode();
70
-  }
71
-
72
-  @Override
73
-  public boolean equals(Object o) {
74
-    if (this == o) return true;
75
-    if (o == null) return false;
76
-    if (!(o instanceof Voyage)) return false;
77
-
78
-    final Voyage that = (Voyage) o;
79
-
80
-    return sameAs(that);
81
-  }
82
-
83
-  @Override
84
-  public boolean sameAs(Voyage other) {
85
-    return other != null && this.voyageNumber().sameValueAs(other.voyageNumber());
86
-  }
87
-
88
-  @Override
89
   public String toString() {
73
   public String toString() {
90
     return "Voyage " + voyageNumber;
74
     return "Voyage " + voyageNumber;
91
   }
75
   }
92
 
76
 
93
   Voyage() {
77
   Voyage() {
94
     // Needed by Hibernate
78
     // Needed by Hibernate
79
+    voyageNumber = null;
95
   }
80
   }
96
 
81
 
97
-  // Needed by Hibernate
98
-  private Long id;
99
-
100
-
101
   /**
82
   /**
102
    * Builder pattern is used for incremental construction
83
    * Builder pattern is used for incremental construction
103
    * of a Voyage aggregate. This serves as an aggregate factory.
84
    * of a Voyage aggregate. This serves as an aggregate factory.

+ 5
- 25
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/model/voyage/VoyageNumber.java 查看文件

1
 package se.citerus.dddsample.tracking.core.domain.model.voyage;
1
 package se.citerus.dddsample.tracking.core.domain.model.voyage;
2
 
2
 
3
 import org.apache.commons.lang.Validate;
3
 import org.apache.commons.lang.Validate;
4
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
4
+import se.citerus.dddsample.tracking.core.domain.shared.experimental.ValueObjectSupport;
5
 
5
 
6
 /**
6
 /**
7
  * Identifies a voyage.
7
  * Identifies a voyage.
8
  */
8
  */
9
-public class VoyageNumber implements ValueObject<VoyageNumber> {
9
+public class VoyageNumber extends ValueObjectSupport<VoyageNumber> {
10
 
10
 
11
-  private String number;
11
+  private final String number;
12
 
12
 
13
-  public VoyageNumber(String number) {
13
+  public VoyageNumber(final String number) {
14
     Validate.notNull(number);
14
     Validate.notNull(number);
15
 
15
 
16
     this.number = number;
16
     this.number = number;
17
   }
17
   }
18
 
18
 
19
   @Override
19
   @Override
20
-  public boolean equals(Object o) {
21
-    if (this == o) return true;
22
-    if (o == null) return false;
23
-    if (!(o instanceof VoyageNumber)) return false;
24
-
25
-    final VoyageNumber other = (VoyageNumber) o;
26
-
27
-    return sameValueAs(other);
28
-  }
29
-
30
-  @Override
31
-  public int hashCode() {
32
-    return number.hashCode();
33
-  }
34
-
35
-  @Override
36
-  public boolean sameValueAs(VoyageNumber other) {
37
-    return other != null && this.number.equals(other.number);
38
-  }
39
-
40
-  @Override
41
   public String toString() {
20
   public String toString() {
42
     return number;
21
     return number;
43
   }
22
   }
48
 
27
 
49
   VoyageNumber() {
28
   VoyageNumber() {
50
     // Needed by Hibernate
29
     // Needed by Hibernate
30
+    number = null;
51
   }
31
   }
52
 
32
 
53
 }
33
 }

+ 8
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/shared/experimental/EntitySupport.java 查看文件

18
     return identity().hashCode();
18
     return identity().hashCode();
19
   }
19
   }
20
 
20
 
21
+  @SuppressWarnings({"SimplifiableIfStatement", "unchecked"})
21
   @Override
22
   @Override
22
   public final boolean equals(final Object o) {
23
   public final boolean equals(final Object o) {
23
     if (this == o) return true;
24
     if (this == o) return true;
24
-    if (o == null || getClass() != o.getClass()) return false;
25
+    // TODO class comparision is too strict for ORM proxies
26
+    if (o == null || !(o instanceof EntitySupport)) return false;
27
+    //if (o == null || getClass() != o.getClass()) return false;
25
 
28
 
26
     return sameAs((T) o);
29
     return sameAs((T) o);
27
   }
30
   }
28
 
31
 
32
+  @SuppressWarnings("UnusedDeclaration")
33
+  // Surrogate primary key
34
+  private Long id;
35
+
29
 }
36
 }

+ 0
- 7
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/shared/experimental/ValueObject.java 查看文件

13
    */
13
    */
14
   boolean sameValueAs(T other);
14
   boolean sameValueAs(T other);
15
 
15
 
16
-  /**
17
-   * Value objects can be freely copied.
18
-   *
19
-   * @return A safe, deep copy of this value object.
20
-   */
21
-  T copy();
22
-
23
 }
16
 }

+ 3
- 1
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/domain/shared/experimental/ValueObjectSupport.java 查看文件

2
 
2
 
3
 import org.apache.commons.lang.builder.EqualsBuilder;
3
 import org.apache.commons.lang.builder.EqualsBuilder;
4
 import org.apache.commons.lang.builder.HashCodeBuilder;
4
 import org.apache.commons.lang.builder.HashCodeBuilder;
5
-import se.citerus.dddsample.tracking.core.domain.shared.ValueObject;
6
 
5
 
7
 /**
6
 /**
8
  * Base class for value objects.
7
  * Base class for value objects.
12
 public abstract class ValueObjectSupport<T extends ValueObject> implements ValueObject<T> {
11
 public abstract class ValueObjectSupport<T extends ValueObject> implements ValueObject<T> {
13
 
12
 
14
   private transient int cachedHashCode = 0;
13
   private transient int cachedHashCode = 0;
14
+  @SuppressWarnings("UnusedDeclaration")
15
+  private final Long id = null;
15
 
16
 
16
   /**
17
   /**
17
    * @param other The other value object.
18
    * @param other The other value object.
49
    * @param o other object
50
    * @param o other object
50
    * @return True if other object has the same value as this value object.
51
    * @return True if other object has the same value as this value object.
51
    */
52
    */
53
+  @SuppressWarnings({"SimplifiableIfStatement", "unchecked"})
52
   @Override
54
   @Override
53
   public final boolean equals(final Object o) {
55
   public final boolean equals(final Object o) {
54
     if (this == o) return true;
56
     if (this == o) return true;

+ 26
- 20
dddsample/tracking/core/src/main/java/se/citerus/dddsample/tracking/core/interfaces/booking/facade/DTOAssembler.java 查看文件

1
 package se.citerus.dddsample.tracking.core.interfaces.booking.facade;
1
 package se.citerus.dddsample.tracking.core.interfaces.booking.facade;
2
 
2
 
3
+import se.citerus.dddsample.tracking.booking.api.CargoRoutingDTO;
4
+import se.citerus.dddsample.tracking.booking.api.LegDTO;
5
+import se.citerus.dddsample.tracking.booking.api.LocationDTO;
6
+import se.citerus.dddsample.tracking.booking.api.RouteCandidateDTO;
3
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
7
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Cargo;
8
+import se.citerus.dddsample.tracking.core.domain.model.cargo.Itinerary;
4
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Leg;
9
 import se.citerus.dddsample.tracking.core.domain.model.cargo.Leg;
5
 import se.citerus.dddsample.tracking.core.domain.model.cargo.RoutingStatus;
10
 import se.citerus.dddsample.tracking.core.domain.model.cargo.RoutingStatus;
6
-import se.citerus.dddsample.tracking.core.domain.model.cargo.Itinerary;
11
+import se.citerus.dddsample.tracking.core.domain.model.location.Location;
12
+import se.citerus.dddsample.tracking.core.domain.model.location.LocationRepository;
13
+import se.citerus.dddsample.tracking.core.domain.model.location.UnLocode;
14
+import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
7
 import se.citerus.dddsample.tracking.core.domain.model.voyage.VoyageNumber;
15
 import se.citerus.dddsample.tracking.core.domain.model.voyage.VoyageNumber;
8
 import se.citerus.dddsample.tracking.core.domain.model.voyage.VoyageRepository;
16
 import se.citerus.dddsample.tracking.core.domain.model.voyage.VoyageRepository;
9
-import se.citerus.dddsample.tracking.core.domain.model.voyage.Voyage;
10
-import se.citerus.dddsample.tracking.core.domain.model.location.UnLocode;
11
-import se.citerus.dddsample.tracking.core.domain.model.location.LocationRepository;
12
-import se.citerus.dddsample.tracking.core.domain.model.location.Location;
13
-import se.citerus.dddsample.tracking.booking.api.CargoRoutingDTO;
14
-import se.citerus.dddsample.tracking.booking.api.LegDTO;
15
-import se.citerus.dddsample.tracking.booking.api.RouteCandidateDTO;
16
-import se.citerus.dddsample.tracking.booking.api.*;
17
 
17
 
18
-import java.util.List;
19
 import java.util.ArrayList;
18
 import java.util.ArrayList;
19
+import java.util.Collections;
20
+import java.util.List;
20
 
21
 
21
 final class DTOAssembler {
22
 final class DTOAssembler {
22
 
23
 
25
    * @return A cargo routing DTO
26
    * @return A cargo routing DTO
26
    */
27
    */
27
   static CargoRoutingDTO toDTO(final Cargo cargo) {
28
   static CargoRoutingDTO toDTO(final Cargo cargo) {
28
-    final List<Leg> legs = cargo.itinerary().legs();
29
+    final Itinerary itinerary = cargo.itinerary();
30
+
31
+    List<LegDTO> legDTOList = Collections.emptyList();
32
+    if (itinerary != null) {
33
+      final List<Leg> legs = itinerary.legs();
29
 
34
 
30
-    final List<LegDTO> legDTOList = new ArrayList<LegDTO>(legs.size());
31
-    for (Leg leg : legs) {
32
-      final LegDTO legDTO = new LegDTO(
33
-        leg.voyage().voyageNumber().stringValue(),
34
-        leg.loadLocation().unLocode().stringValue(),
35
-        leg.unloadLocation().unLocode().stringValue(),
36
-        leg.loadTime(),
37
-        leg.unloadTime());
38
-      legDTOList.add(legDTO);
35
+      legDTOList = new ArrayList<LegDTO>(legs.size());
36
+      for (Leg leg : legs) {
37
+        final LegDTO legDTO = new LegDTO(
38
+          leg.voyage().voyageNumber().stringValue(),
39
+          leg.loadLocation().unLocode().stringValue(),
40
+          leg.unloadLocation().unLocode().stringValue(),
41
+          leg.loadTime(),
42
+          leg.unloadTime());
43
+        legDTOList.add(legDTO);
44
+      }
39
     }
45
     }
40
 
46
 
41
     return new CargoRoutingDTO(
47
     return new CargoRoutingDTO(

+ 0
- 5
dddsample/tracking/core/src/test/java/se/citerus/dddsample/tracking/core/domain/model/cargo/ItineraryTest.java 查看文件

89
 
89
 
90
     HandlingActivity claimRotterdam = new HandlingActivity(CLAIM, ROTTERDAM);
90
     HandlingActivity claimRotterdam = new HandlingActivity(CLAIM, ROTTERDAM);
91
     assertFalse(itinerary.isExpected(claimRotterdam));
91
     assertFalse(itinerary.isExpected(claimRotterdam));
92
-
93
-    //Unrouted Cargo shouldn't go anywhere or do anything
94
-    assertFalse(Itinerary.EMPTY_ITINERARY.isExpected(receiveShanghai));
95
-    assertFalse(Itinerary.EMPTY_ITINERARY.isExpected(loadShanghai));
96
-
97
   }
92
   }
98
 
93
 
99
   public void testCreateItinerary() throws Exception {
94
   public void testCreateItinerary() throws Exception {