Kaynağa Gözat

Using "notifyOf..." naming convention in ApplicationEvents.

HandlingEvents now have a unique sequence number, and CargoUpdater looks events based on that to update the cargo in question.

First version of pessimistic locking of cargo during rerouting.

Cargo is now a facade for aggregate-internal classes Delivery and Projections.

Delivery calculates current voyage, last known location and transport status on the fly.

Introduced a tracking id generator domain service for getting next tracking id, instead of doing it in the cargo repository.
peter_backlund 17 yıl önce
ebeveyn
işleme
f64ba77f42
47 değiştirilmiş dosya ile 852 ekleme ve 688 silme
  1. 16
    0
      dddsample/src/main/java/se/citerus/dddsample/application/CargoLockingService.java
  2. 51
    0
      dddsample/src/main/java/se/citerus/dddsample/application/CargoLockingServiceInMem.java
  3. 2
    2
      dddsample/src/main/java/se/citerus/dddsample/application/event/ApplicationEvents.java
  4. 0
    43
      dddsample/src/main/java/se/citerus/dddsample/application/event/CargoDeliveryUpdater.java
  5. 59
    0
      dddsample/src/main/java/se/citerus/dddsample/application/event/CargoUpdater.java
  6. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/application/event/MisdirectedNotifier.java
  7. 5
    1
      dddsample/src/main/java/se/citerus/dddsample/application/event/ReadyToClaimNotfier.java
  8. 17
    5
      dddsample/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java
  9. 2
    1
      dddsample/src/main/java/se/citerus/dddsample/application/impl/HandlingEventServiceImpl.java
  10. 39
    39
      dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java
  11. 42
    35
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Cargo.java
  12. 4
    4
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/CargoFactory.java
  13. 0
    4
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/CargoRepository.java
  14. 56
    77
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Delivery.java
  15. 120
    152
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Projections.java
  16. 6
    0
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/TrackingId.java
  17. 10
    0
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/TrackingIdGenerator.java
  18. 37
    19
      dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEvent.java
  19. 7
    2
      dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEventRepository.java
  20. 51
    0
      dddsample/src/main/java/se/citerus/dddsample/domain/model/shared/EventSequenceNumber.java
  21. 6
    6
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/messaging/jms/JmsApplicationEventsImpl.java
  22. 32
    0
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/DatabaseTrackingIdGenerator.java
  23. 23
    0
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/TrackingIdGeneratorInMem.java
  24. 3
    9
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.java
  25. 13
    4
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryHibernate.java
  26. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/CargoRoutingDTOAssembler.java
  27. 8
    10
      dddsample/src/main/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapter.java
  28. 1
    1
      dddsample/src/main/resources/context-application.xml
  29. 1
    1
      dddsample/src/main/resources/context-domain.xml
  30. 6
    6
      dddsample/src/main/resources/context-infrastructure-messaging.xml
  31. 2
    0
      dddsample/src/main/resources/context-infrastructure-persistence.xml
  32. 12
    10
      dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml
  33. 4
    1
      dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEvent.hbm.xml
  34. 8
    8
      dddsample/src/test/java/se/citerus/dddsample/application/BookingServiceTest.java
  35. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/application/HandlingEventServiceTest.java
  36. 2
    2
      dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoFactoryTest.java
  37. 57
    114
      dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoTest.java
  38. 16
    17
      dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/DeliveryTest.java
  39. 22
    0
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/TrackingIdGeneratorTest.java
  40. 0
    11
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java
  41. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryTest.java
  42. 1
    1
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/VoyageRepositoryTest.java
  43. 1
    11
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/CargoRepositoryInMem.java
  44. 14
    0
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/HandlingEventRepositoryInMem.java
  45. 5
    8
      dddsample/src/test/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapterTest.java
  46. 75
    74
      dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java
  47. 12
    6
      dddsample/src/test/java/se/citerus/dddsample/scenario/VoyageRescheduledScenarioTest.java

+ 16
- 0
dddsample/src/main/java/se/citerus/dddsample/application/CargoLockingService.java Dosyayı Görüntüle

@@ -0,0 +1,16 @@
1
+package se.citerus.dddsample.application;
2
+
3
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
4
+
5
+/**
6
+ *
7
+ */
8
+public interface CargoLockingService {
9
+
10
+  void assertLocked(TrackingId trackingId);
11
+
12
+  void unlock(TrackingId trackingId);
13
+
14
+  void lock(TrackingId trackingId);
15
+
16
+}

+ 51
- 0
dddsample/src/main/java/se/citerus/dddsample/application/CargoLockingServiceInMem.java Dosyayı Görüntüle

@@ -0,0 +1,51 @@
1
+/**
2
+ * Purpose
3
+ * @author peter
4
+ * @created 2009-sep-07
5
+ * $Id$
6
+ */
7
+package se.citerus.dddsample.application;
8
+
9
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
10
+
11
+import java.util.HashMap;
12
+import java.util.Map;
13
+import java.util.concurrent.locks.Lock;
14
+import java.util.concurrent.locks.ReentrantLock;
15
+
16
+public class CargoLockingServiceInMem implements CargoLockingService {
17
+
18
+  private final Map<TrackingId, Lock> locks = new HashMap<TrackingId, Lock>();
19
+
20
+  @Override
21
+  public void assertLocked(final TrackingId trackingId) {
22
+    final Lock lock = getLockFor(trackingId);
23
+    if (!lock.tryLock()) {
24
+      throw new RuntimeException("Lock not held");
25
+    }
26
+  }
27
+
28
+  @Override
29
+  public void unlock(final TrackingId trackingId) {
30
+    final Lock lock = getLockFor(trackingId);
31
+    lock.unlock();
32
+  }
33
+
34
+  @Override
35
+  public void lock(final TrackingId trackingId) {
36
+    final Lock lock = getLockFor(trackingId);
37
+    if (!lock.tryLock()) {
38
+      throw new RuntimeException("Could not lock");
39
+    }
40
+  }
41
+
42
+  private Lock getLockFor(final TrackingId trackingId) {
43
+    Lock lock = locks.get(trackingId);
44
+    if (lock == null) {
45
+      lock = new ReentrantLock();
46
+      locks.put(trackingId, lock);
47
+    }
48
+    return lock;
49
+  }
50
+
51
+}

+ 2
- 2
dddsample/src/main/java/se/citerus/dddsample/application/event/ApplicationEvents.java Dosyayı Görüntüle

@@ -17,13 +17,13 @@ public interface ApplicationEvents {
17 17
    *
18 18
    * @param event handling event
19 19
    */
20
-  void cargoWasHandled(HandlingEvent event);
20
+  void notifyOfHandlingEvent(HandlingEvent event);
21 21
 
22 22
   /**
23 23
    * Cargo delivery has been updated.
24 24
    *
25 25
    * @param cargo cargo
26 26
    */
27
-  void cargoDeliveryWasUpdated(Cargo cargo);
27
+  void notifyOfCargoUpdate(Cargo cargo);
28 28
 
29 29
 }

+ 0
- 43
dddsample/src/main/java/se/citerus/dddsample/application/event/CargoDeliveryUpdater.java Dosyayı Görüntüle

@@ -1,43 +0,0 @@
1
-package se.citerus.dddsample.application.event;
2
-
3
-import org.apache.commons.logging.Log;
4
-import org.apache.commons.logging.LogFactory;
5
-import org.springframework.transaction.annotation.Transactional;
6
-import se.citerus.dddsample.domain.model.cargo.Cargo;
7
-import se.citerus.dddsample.domain.model.cargo.CargoRepository;
8
-import se.citerus.dddsample.domain.model.cargo.TrackingId;
9
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
10
-import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
11
-
12
-public class CargoDeliveryUpdater {
13
-
14
-  private ApplicationEvents applicationEvents;
15
-  private CargoRepository cargoRepository;
16
-  private HandlingEventRepository handlingEventRepository;
17
-  private final Log logger = LogFactory.getLog(getClass());
18
-
19
-  public CargoDeliveryUpdater(final ApplicationEvents applicationEvents,
20
-                              final CargoRepository cargoRepository,
21
-                              final HandlingEventRepository handlingEventRepository) {
22
-    this.applicationEvents = applicationEvents;
23
-    this.cargoRepository = cargoRepository;
24
-    this.handlingEventRepository = handlingEventRepository;
25
-  }
26
-
27
-  @Transactional
28
-  public void updateDelivery(final TrackingId trackingId) {
29
-    final Cargo cargo = cargoRepository.find(trackingId);
30
-    final HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
31
-
32
-    // TODO still doesn't sound right...cargo.updateDelivery()?
33
-    cargo.handled(handlingEvent.handlingActivity());
34
-
35
-    cargoRepository.store(cargo);
36
-    applicationEvents.cargoDeliveryWasUpdated(cargo);
37
-    logger.info("Updated delivery of cargo " + cargo + ": " + cargo.delivery());
38
-  }
39
-
40
-  CargoDeliveryUpdater() {
41
-  }
42
-
43
-}

+ 59
- 0
dddsample/src/main/java/se/citerus/dddsample/application/event/CargoUpdater.java Dosyayı Görüntüle

@@ -0,0 +1,59 @@
1
+package se.citerus.dddsample.application.event;
2
+
3
+import org.apache.commons.logging.Log;
4
+import org.apache.commons.logging.LogFactory;
5
+import org.springframework.transaction.annotation.Transactional;
6
+import se.citerus.dddsample.domain.model.cargo.Cargo;
7
+import se.citerus.dddsample.domain.model.cargo.CargoRepository;
8
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9
+import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
10
+import se.citerus.dddsample.domain.model.shared.EventSequenceNumber;
11
+import se.citerus.dddsample.domain.model.shared.HandlingActivity;
12
+
13
+public class CargoUpdater {
14
+
15
+  private ApplicationEvents applicationEvents;
16
+  private CargoRepository cargoRepository;
17
+  private HandlingEventRepository handlingEventRepository;
18
+  private final Log logger = LogFactory.getLog(getClass());
19
+
20
+  public CargoUpdater(final ApplicationEvents applicationEvents,
21
+                      final CargoRepository cargoRepository,
22
+                      final HandlingEventRepository handlingEventRepository) {
23
+    this.applicationEvents = applicationEvents;
24
+    this.cargoRepository = cargoRepository;
25
+    this.handlingEventRepository = handlingEventRepository;
26
+  }
27
+
28
+  @Transactional
29
+  public void updateCargo(final EventSequenceNumber eventSequenceNumber) {
30
+    final HandlingEvent handlingEvent = handlingEventRepository.find(eventSequenceNumber);
31
+    final HandlingActivity activity = handlingEvent.activity();
32
+    final Cargo cargo = handlingEvent.cargo();
33
+
34
+    // TODO create domain events and deal with them as a result of the handling
35
+    cargo.handled(activity);
36
+
37
+    /*
38
+    Here's an idea:
39
+
40
+    ResultOfHandling result = cargo.effectOf(activity);
41
+    cargo.apply(result);
42
+
43
+    or
44
+
45
+    cargo.handled(activity);
46
+    Delivery delivery = cargo.currentDelivery();
47
+    send all delivery.events();
48
+
49
+    */
50
+
51
+    cargoRepository.store(cargo);
52
+    applicationEvents.notifyOfCargoUpdate(cargo);
53
+    logger.info("Updated delivery of cargo " + cargo);
54
+  }
55
+
56
+  CargoUpdater() {
57
+  }
58
+
59
+}

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/application/event/MisdirectedNotifier.java Dosyayı Görüntüle

@@ -27,7 +27,7 @@ public class MisdirectedNotifier {
27 27
   public void alertIfMisdirected(final TrackingId trackingId) {
28 28
     final Cargo cargo = cargoRepository.find(trackingId);
29 29
 
30
-    if (cargo.delivery().isMisdirected()) {
30
+    if (cargo.isMisdirected()) {
31 31
       LOG.info("Cargo " + cargo + " is misdirected!");
32 32
     }
33 33
   }

+ 5
- 1
dddsample/src/main/java/se/citerus/dddsample/application/event/ReadyToClaimNotfier.java Dosyayı Görüntüle

@@ -26,7 +26,11 @@ public class ReadyToClaimNotfier {
26 26
   public void alertIfReadyToClaim(final TrackingId trackingId) {
27 27
     final Cargo cargo = cargoRepository.find(trackingId);
28 28
                                                       
29
-    if (cargo.delivery().isUnloadedAtDestination()) {
29
+    if (cargo.isReadyToClaim()) {
30
+      /**
31
+       * At this point, a real system would probably send an email or SMS
32
+       * or something, but we simply log a message.
33
+       */
30 34
       LOG.info("Cargo " + cargo + " is ready to be claimed");
31 35
     }
32 36
   }

+ 17
- 5
dddsample/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java Dosyayı Görüntüle

@@ -5,6 +5,8 @@ import org.apache.commons.logging.Log;
5 5
 import org.apache.commons.logging.LogFactory;
6 6
 import org.springframework.transaction.annotation.Transactional;
7 7
 import se.citerus.dddsample.application.BookingService;
8
+import se.citerus.dddsample.application.CargoLockingService;
9
+import se.citerus.dddsample.application.CargoLockingServiceInMem;
8 10
 import se.citerus.dddsample.domain.model.cargo.*;
9 11
 import se.citerus.dddsample.domain.model.location.Location;
10 12
 import se.citerus.dddsample.domain.model.location.LocationRepository;
@@ -21,6 +23,7 @@ public final class BookingServiceImpl implements BookingService {
21 23
   private final CargoFactory cargoFactory;
22 24
   private final CargoRepository cargoRepository;
23 25
   private final LocationRepository locationRepository;
26
+  private final CargoLockingService cargoLockingService;
24 27
   private final Log logger = LogFactory.getLog(getClass());
25 28
 
26 29
   public BookingServiceImpl(final RoutingService routingService,
@@ -31,6 +34,7 @@ public final class BookingServiceImpl implements BookingService {
31 34
     this.cargoFactory = cargoFactory;
32 35
     this.cargoRepository = cargoRepository;
33 36
     this.locationRepository = locationRepository;
37
+    this.cargoLockingService = new CargoLockingServiceInMem();
34 38
   }
35 39
 
36 40
   @Override
@@ -46,7 +50,7 @@ public final class BookingServiceImpl implements BookingService {
46 50
   }
47 51
 
48 52
   @Override
49
-  @Transactional
53
+  @Transactional(readOnly = true)
50 54
   public List<Itinerary> requestPossibleRoutesForCargo(final TrackingId trackingId) {
51 55
     final Cargo cargo = cargoRepository.find(trackingId);
52 56
 
@@ -60,19 +64,23 @@ public final class BookingServiceImpl implements BookingService {
60 64
   @Override
61 65
   @Transactional
62 66
   public void assignCargoToRoute(final Itinerary itinerary, final TrackingId trackingId) {
63
-    // TODO locking semantics
67
+    cargoLockingService.assertLocked(trackingId);
68
+
64 69
     final Cargo cargo = cargoRepository.find(trackingId);
65 70
     Validate.notNull(cargo, "Can't assign itinerary to non-existing cargo " + trackingId);
66 71
     cargo.assignToRoute(itinerary);
67 72
     cargoRepository.store(cargo);
68 73
 
69 74
     logger.info("Assigned cargo " + trackingId + " to new route");
75
+
76
+    cargoLockingService.unlock(trackingId);
70 77
   }
71 78
 
72 79
   @Override
73 80
   @Transactional
74 81
   public void changeDestination(final TrackingId trackingId, final UnLocode unLocode) {
75
-    // TODO locking semantics
82
+    cargoLockingService.assertLocked(trackingId);
83
+
76 84
     final Cargo cargo = cargoRepository.find(trackingId);
77 85
     Validate.notNull(cargo, "Can't change destination of non-existing cargo " + trackingId);
78 86
     final Location newDestination = locationRepository.find(unLocode);
@@ -82,13 +90,17 @@ public final class BookingServiceImpl implements BookingService {
82 90
 
83 91
     cargoRepository.store(cargo);
84 92
     logger.info("Changed destination for cargo " + trackingId + " to " + routeSpecification.destination());
93
+
94
+    cargoLockingService.unlock(trackingId);
85 95
   }
86 96
 
87 97
   @Override
88
-  @Transactional
98
+  @Transactional(readOnly = true)
89 99
   public Cargo loadCargoForRouting(final TrackingId trackingId) {
90
-    // TODO locking semantics
91 100
     final Cargo cargo = cargoRepository.find(trackingId);
101
+    if (cargo != null) {
102
+      cargoLockingService.lock(trackingId);
103
+    }
92 104
     return cargo;
93 105
   }
94 106
 

+ 2
- 1
dddsample/src/main/java/se/citerus/dddsample/application/impl/HandlingEventServiceImpl.java Dosyayı Görüntüle

@@ -37,6 +37,7 @@ public final class HandlingEventServiceImpl implements HandlingEventService {
37 37
                                     final VoyageNumber voyageNumber,
38 38
                                     final UnLocode unLocode,
39 39
                                     final HandlingEvent.Type type) throws CannotCreateHandlingEventException {
40
+
40 41
     final Date registrationTime = new Date();
41 42
     /* Using a factory to create a HandlingEvent (aggregate). This is where
42 43
        it is determined wether the incoming data, the attempt, actually is capable
@@ -52,7 +53,7 @@ public final class HandlingEventServiceImpl implements HandlingEventService {
52 53
     handlingEventRepository.store(event);
53 54
 
54 55
     /* Publish an event stating that a cargo has been handled. */
55
-    applicationEvents.cargoWasHandled(event);
56
+    applicationEvents.notifyOfHandlingEvent(event);
56 57
 
57 58
     logger.info("Registered handling event");
58 59
   }

+ 39
- 39
dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java Dosyayı Görüntüle

@@ -50,48 +50,48 @@ public class SampleDataGenerator implements ServletContextListener {
50 50
 
51 51
   private static void loadHandlingEventData(JdbcTemplate jdbcTemplate) {
52 52
     String handlingEventSql =
53
-      "insert into HandlingEvent (completionTime, registrationTime, type, location_id, voyage_id, cargo_id) " +
54
-        "values (?, ?, ?, ?, ?, ?)";
53
+      "insert into HandlingEvent (sequence_number, completionTime, registrationTime, type, location_id, voyage_id, cargo_id) " +
54
+        "values (?, ?, ?, ?, ?, ?, ?)";
55 55
 
56 56
     Object[][] handlingEventArgs = {
57 57
       //XYZ (SESTO-FIHEL-DEHAM-CNHKG-JPTOK-AUMEL)
58
-      {ts(0), ts((0)), "RECEIVE", 1, null, 1},
59
-      {ts((4)), ts((5)), "LOAD", 1, 1, 1},
60
-      {ts((14)), ts((14)), "UNLOAD", 5, 1, 1},
61
-      {ts((15)), ts((15)), "LOAD", 5, 1, 1},
62
-      {ts((30)), ts((30)), "UNLOAD", 6, 1, 1},
63
-      {ts((33)), ts((33)), "LOAD", 6, 1, 1},
64
-      {ts((34)), ts((34)), "UNLOAD", 3, 1, 1},
65
-      {ts((60)), ts((60)), "LOAD", 3, 1, 1},
66
-      {ts((70)), ts((71)), "UNLOAD", 4, 1, 1},
67
-      {ts((75)), ts((75)), "LOAD", 4, 1, 1},
68
-      {ts((88)), ts((88)), "UNLOAD", 2, 1, 1},
69
-      {ts((100)), ts((102)), "CLAIM", 2, null, 1},
58
+      {1, ts(0), ts((0)), "RECEIVE", 1, null, 1},
59
+      {2, ts((4)), ts((5)), "LOAD", 1, 1, 1},
60
+      {3, ts((14)), ts((14)), "UNLOAD", 5, 1, 1},
61
+      {4, ts((15)), ts((15)), "LOAD", 5, 1, 1},
62
+      {5, ts((30)), ts((30)), "UNLOAD", 6, 1, 1},
63
+      {6, ts((33)), ts((33)), "LOAD", 6, 1, 1},
64
+      {7, ts((34)), ts((34)), "UNLOAD", 3, 1, 1},
65
+      {8, ts((60)), ts((60)), "LOAD", 3, 1, 1},
66
+      {9, ts((70)), ts((71)), "UNLOAD", 4, 1, 1},
67
+      {10, ts((75)), ts((75)), "LOAD", 4, 1, 1},
68
+      {11, ts((88)), ts((88)), "UNLOAD", 2, 1, 1},
69
+      {12, ts((100)), ts((102)), "CLAIM", 2, null, 1},
70 70
 
71 71
       //ZYX (AUMEL - USCHI - DEHAM -)
72
-      {ts((200)), ts((201)), "RECEIVE", 2, null, 3},
73
-      {ts((202)), ts((202)), "LOAD", 2, 2, 3},
74
-      {ts((208)), ts((208)), "UNLOAD", 7, 2, 3},
75
-      {ts((212)), ts((212)), "LOAD", 7, 2, 3},
76
-      {ts((230)), ts((230)), "UNLOAD", 6, 2, 3},
77
-      {ts((235)), ts((235)), "LOAD", 6, 2, 3},
72
+      {13, ts((200)), ts((201)), "RECEIVE", 2, null, 3},
73
+      {14, ts((202)), ts((202)), "LOAD", 2, 2, 3},
74
+      {15, ts((208)), ts((208)), "UNLOAD", 7, 2, 3},
75
+      {16, ts((212)), ts((212)), "LOAD", 7, 2, 3},
76
+      {17, ts((230)), ts((230)), "UNLOAD", 6, 2, 3},
77
+      {18, ts((235)), ts((235)), "LOAD", 6, 2, 3},
78 78
 
79 79
       //ABC
80
-      {ts((20)), ts((21)), "CLAIM", 2, null, 2},
80
+      {19, ts((20)), ts((21)), "CLAIM", 2, null, 2},
81 81
 
82 82
       //CBA
83
-      {ts((0)), ts((1)), "RECEIVE", 2, null, 4},
84
-      {ts((10)), ts((11)), "LOAD", 2, 2, 4},
85
-      {ts((20)), ts((21)), "UNLOAD", 7, 2, 4},
83
+      {20, ts((0)), ts((1)), "RECEIVE", 2, null, 4},
84
+      {21, ts((10)), ts((11)), "LOAD", 2, 2, 4},
85
+      {22, ts((20)), ts((21)), "UNLOAD", 7, 2, 4},
86 86
 
87 87
       //FGH
88
-      {ts(100), ts(160), "RECEIVE", 3, null, 5},
89
-      {ts(150), ts(110), "LOAD", 3, 3, 5},
88
+      {23, ts(100), ts(160), "RECEIVE", 3, null, 5},
89
+      {24, ts(150), ts(110), "LOAD", 3, 3, 5},
90 90
 
91 91
       // JKL
92
-      {ts(200), ts(220), "RECEIVE", 6, null, 6},
93
-      {ts(300), ts(330), "LOAD", 6, 3, 6},
94
-      {ts(400), ts(440), "UNLOAD", 5, 3, 6}  // Unexpected event
92
+      {25, ts(200), ts(220), "RECEIVE", 6, null, 6},
93
+      {26, ts(300), ts(330), "LOAD", 6, 3, 6},
94
+      {27, ts(400), ts(440), "UNLOAD", 5, 3, 6}  // Unexpected event
95 95
     };
96 96
     executeUpdate(jdbcTemplate, handlingEventSql, handlingEventArgs);
97 97
   }
@@ -136,16 +136,16 @@ public class SampleDataGenerator implements ServletContextListener {
136 136
 
137 137
   private static void loadCargoData(JdbcTemplate jdbcTemplate) {
138 138
     String cargoSql =
139
-      "insert into Cargo (id, tracking_id, spec_origin_id, spec_destination_id, spec_arrival_deadline, transport_status, current_voyage_id, last_known_location_id, is_misdirected, routing_status, calculated_at, unloaded_at_dest) " +
140
-        "values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
139
+      "insert into Cargo (id, tracking_id, spec_origin_id, spec_destination_id, spec_arrival_deadline, is_misdirected, routing_status, calculated_at, unloaded_at_dest) " +
140
+        "values (?, ?, ?, ?, ?, ?, ?, ?, ?)";
141 141
 
142 142
     Object[][] cargoArgs = {
143
-      {1, "XYZ", 1, 2, ts(10), "IN_PORT", null, 1, false, "ROUTED", ts(100), false},
144
-      {2, "ABC", 1, 5, ts(20), "IN_PORT", null, 1, false, "ROUTED", ts(100), false},
145
-      {3, "ZYX", 2, 1, ts(30), "IN_PORT", null, 1, false, "NOT_ROUTED", ts(100), false},
146
-      {4, "CBA", 5, 1, ts(40), "IN_PORT", null, 1, false, "MISROUTED", ts(100), false},
147
-      {5, "FGH", 3, 5, ts(50), "IN_PORT", null, 1, false, "ROUTED", ts(100), false},  // Cargo origin differs from spec origin
148
-      {6, "JKL", 6, 4, ts(60), "IN_PORT", null, 1, true, "ROUTED", ts(100), false}
143
+      {1, "XYZ", 1, 2, ts(10), false, "ROUTED", ts(100), false},
144
+      {2, "ABC", 1, 5, ts(20), false, "ROUTED", ts(100), false},
145
+      {3, "ZYX", 2, 1, ts(30), false, "NOT_ROUTED", ts(100), false},
146
+      {4, "CBA", 5, 1, ts(40), false, "MISROUTED", ts(100), false},
147
+      {5, "FGH", 3, 5, ts(50), false, "ROUTED", ts(100), false},  // Cargo origin differs from spec origin
148
+      {6, "JKL", 6, 4, ts(60), true, "ROUTED", ts(100), false}
149 149
     };
150 150
     executeUpdate(jdbcTemplate, cargoSql, cargoArgs);
151 151
   }
@@ -254,7 +254,7 @@ public class SampleDataGenerator implements ServletContextListener {
254 254
         }
255 255
 
256 256
         final HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(abc123);
257
-        abc123.handled(handlingEvent.handlingActivity());
257
+        abc123.handled(handlingEvent.activity());
258 258
         session.update(abc123);
259 259
 
260 260
         // Cargo JKL567
@@ -298,7 +298,7 @@ public class SampleDataGenerator implements ServletContextListener {
298 298
         }
299 299
 
300 300
         HandlingEvent handlingEvent1 = handlingEventRepository.mostRecentHandling(jkl567);
301
-        jkl567.handled(handlingEvent1.handlingActivity());
301
+        jkl567.handled(handlingEvent1.activity());
302 302
         session.update(jkl567);
303 303
       }
304 304
     });

+ 42
- 35
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Cargo.java Dosyayı Görüntüle

@@ -2,13 +2,15 @@ package se.citerus.dddsample.domain.model.cargo;
2 2
 
3 3
 import org.apache.commons.lang.Validate;
4 4
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
5
-import se.citerus.dddsample.domain.model.handling.HandlingHistory;
6 5
 import se.citerus.dddsample.domain.model.location.CustomsZone;
7 6
 import se.citerus.dddsample.domain.model.location.Location;
8 7
 import se.citerus.dddsample.domain.model.shared.HandlingActivity;
8
+import se.citerus.dddsample.domain.model.voyage.Voyage;
9 9
 import se.citerus.dddsample.domain.shared.DomainObjectUtils;
10 10
 import se.citerus.dddsample.domain.shared.Entity;
11 11
 
12
+import java.util.Date;
13
+
12 14
 /**
13 15
  * A Cargo. This is the central class in the domain model,
14 16
  * and it is the root of the Cargo-Itinerary-Leg-Delivery-RouteSpecification aggregate.
@@ -59,7 +61,7 @@ public class Cargo implements Entity<Cargo> {
59 61
     this.trackingId = trackingId;
60 62
     this.routeSpecification = routeSpecification;
61 63
     this.delivery = Delivery.initial(routeSpecification, itinerary);
62
-    this.projections = new Projections(delivery, itinerary, routeSpecification);
64
+    this.projections = new Projections(delivery, itinerary);
63 65
   }
64 66
 
65 67
   /**
@@ -72,13 +74,6 @@ public class Cargo implements Entity<Cargo> {
72 74
   }
73 75
 
74 76
   /**
75
-   * @return The delivery. Never null.
76
-   */
77
-  public Delivery delivery() {
78
-    return delivery;
79
-  }
80
-
81
-  /**
82 77
    * @return The itinerary. Never null.
83 78
    */
84 79
   public Itinerary itinerary() {
@@ -92,11 +87,32 @@ public class Cargo implements Entity<Cargo> {
92 87
     return routeSpecification;
93 88
   }
94 89
 
95
-  /**
96
-   * @return The projections for this cargo.
97
-   */
98
-  public Projections projections() {
99
-    return projections;
90
+  public Date estimatedTimeOfArrival() {
91
+    return projections.estimatedTimeOfArrival();
92
+  }
93
+
94
+  public HandlingActivity nextExpectedActivity() {
95
+    return projections.nextExpectedActivity();
96
+  }
97
+
98
+  public boolean isMisdirected() {
99
+    return delivery.isMisdirected();
100
+  }
101
+
102
+  public TransportStatus transportStatus() {
103
+    return delivery.transportStatus();
104
+  }
105
+
106
+  public RoutingStatus routingStatus() {
107
+    return delivery.routingStatus();
108
+  }
109
+
110
+  public Voyage currentVoyage() {
111
+    return delivery.currentVoyage();
112
+  }
113
+
114
+  public Location lastKnownLocation() {
115
+    return delivery.lastKnownLocation();
100 116
   }
101 117
 
102 118
   /**
@@ -109,8 +125,8 @@ public class Cargo implements Entity<Cargo> {
109 125
 
110 126
     this.routeSpecification = routeSpecification;
111 127
     // Handling consistency within the Cargo aggregate synchronously
112
-    this.delivery = delivery.withRoutingChange(this.routeSpecification, this.itinerary);
113
-    this.projections = new Projections(delivery, itinerary, routeSpecification);
128
+    this.delivery = delivery.withRoutingChange(routeSpecification, itinerary);
129
+    this.projections = new Projections(delivery, itinerary);
114 130
   }
115 131
 
116 132
   /**
@@ -123,8 +139,8 @@ public class Cargo implements Entity<Cargo> {
123 139
 
124 140
     this.itinerary = itinerary;
125 141
     // Handling consistency within the Cargo aggregate synchronously
126
-    this.delivery = delivery.withRoutingChange(this.routeSpecification, this.itinerary);
127
-    this.projections = new Projections(delivery, itinerary, routeSpecification);
142
+    this.delivery = delivery.withRoutingChange(routeSpecification, itinerary);
143
+    this.projections = new Projections(delivery, itinerary);
128 144
   }
129 145
 
130 146
   public CustomsZone customsZone() {
@@ -136,6 +152,10 @@ public class Cargo implements Entity<Cargo> {
136 152
     return customsZone().entryPoint(itinerary.locations());
137 153
   }
138 154
 
155
+  public boolean isReadyToClaim() {
156
+    return delivery.isUnloadedAtDestination();
157
+  }
158
+
139 159
   /**
140 160
    * Updates all aspects of the cargo aggregate status
141 161
    * based on the current route specification, itinerary and handling of the cargo.
@@ -146,30 +166,17 @@ public class Cargo implements Entity<Cargo> {
146 166
    * <p/>
147 167
    * {@link RouteSpecification} and {@link Itinerary} are both inside the Cargo
148 168
    * aggregate, so changes to them cause the status to be updated <b>synchronously</b>,
149
-   * but changes to the delivery history (when a cargo is handled) cause the status update
150
-   * to happen <b>asynchronously</b> since {@link HandlingEvent} is in a different aggregate.
169
+   * but handling cause the status update to happen <b>asynchronously</b>
170
+   * since {@link HandlingEvent} is in a different aggregate.
151 171
    *
152
-   * @param handlingHistory handling history
172
+   * @param handlingActivity handling activity
153 173
    */
154
-  // TODO Under migration, this method will be removed and replaced with the handled() method
155
-  public void deriveDeliveryProgress(final HandlingHistory handlingHistory) {
156
-    Validate.isTrue(this.sameIdentityAs(handlingHistory.cargo()),
157
-      "Handling history must refer to this cargo, " + this + ". " +
158
-        "Given handlig history refers to cargo " + handlingHistory.cargo());
159
-
160
-    final HandlingEvent handlingEvent = handlingHistory.mostRecentPhysicalHandling();
161
-    if (handlingEvent != null) {
162
-      HandlingActivity handlingActivity = handlingEvent.handlingActivity();
163
-      handled(handlingActivity);
164
-    }
165
-  }
166
-
167 174
   public void handled(final HandlingActivity handlingActivity) {
168 175
     Validate.notNull(handlingActivity, "Handling activity is required");
169 176
 
170 177
     // Delivery and Projections are value object, so they are replaced with new or derived ones
171 178
     this.delivery = delivery.whenHandled(routeSpecification, itinerary, handlingActivity);
172
-    this.projections = new Projections(delivery, itinerary, routeSpecification, handlingActivity);
179
+    this.projections = new Projections(delivery, itinerary);
173 180
   }
174 181
 
175 182
   @Override

+ 4
- 4
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/CargoFactory.java Dosyayı Görüntüle

@@ -14,16 +14,16 @@ import java.util.Date;
14 14
 
15 15
 public class CargoFactory {
16 16
 
17
-  private final CargoRepository cargoRepository;
18 17
   private final LocationRepository locationRepository;
18
+  private final TrackingIdGenerator trackingIdGenerator;
19 19
 
20
-  public CargoFactory(CargoRepository cargoRepository, LocationRepository locationRepository) {
21
-    this.cargoRepository = cargoRepository;
20
+  public CargoFactory(LocationRepository locationRepository, TrackingIdGenerator trackingIdGenerator) {
22 21
     this.locationRepository = locationRepository;
22
+    this.trackingIdGenerator = trackingIdGenerator;
23 23
   }
24 24
 
25 25
   public Cargo newCargo(UnLocode originUnLocode, UnLocode destinationUnLocode, Date arrivalDeadline) {
26
-    final TrackingId trackingId = cargoRepository.nextTrackingId();
26
+    final TrackingId trackingId = trackingIdGenerator.nextTrackingId();
27 27
     final Location origin = locationRepository.find(originUnLocode);
28 28
     final Location destination = locationRepository.find(destinationUnLocode);
29 29
     final RouteSpecification routeSpecification = new RouteSpecification(origin, destination, arrivalDeadline);

+ 0
- 4
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/CargoRepository.java Dosyayı Görüntüle

@@ -35,8 +35,4 @@ public interface CargoRepository {
35 35
    */
36 36
   void store(Cargo cargo);
37 37
 
38
-  /**
39
-   * @return A unique, generated tracking Id.
40
-   */
41
-  TrackingId nextTrackingId();
42 38
 }

+ 56
- 77
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Delivery.java Dosyayı Görüntüle

@@ -3,13 +3,14 @@ package se.citerus.dddsample.domain.model.cargo;
3 3
 import org.apache.commons.lang.Validate;
4 4
 import org.apache.commons.lang.builder.EqualsBuilder;
5 5
 import org.apache.commons.lang.builder.HashCodeBuilder;
6
-import static se.citerus.dddsample.domain.model.cargo.TransportStatus.*;
7
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
8
-import se.citerus.dddsample.domain.model.handling.HandlingHistory;
6
+import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.ROUTED;
7
+import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.derivedFrom;
8
+import static se.citerus.dddsample.domain.model.cargo.TransportStatus.ONBOARD_CARRIER;
9
+import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.CLAIM;
10
+import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.UNLOAD;
9 11
 import se.citerus.dddsample.domain.model.location.Location;
10 12
 import se.citerus.dddsample.domain.model.shared.HandlingActivity;
11 13
 import se.citerus.dddsample.domain.model.voyage.Voyage;
12
-import se.citerus.dddsample.domain.shared.DomainObjectUtils;
13 14
 import se.citerus.dddsample.domain.shared.ValueObject;
14 15
 
15 16
 import java.util.Date;
@@ -20,13 +21,13 @@ import java.util.Date;
20 21
  */
21 22
 public class Delivery implements ValueObject<Delivery> {
22 23
 
23
-  private TransportStatus transportStatus;
24
-  private Location lastKnownLocation;
25
-  private Voyage currentVoyage;
24
+  private HandlingActivity mostRecentHandlingActivity;
25
+  private Date calculatedAt;
26
+
27
+  // TODO these will be replaced by domain events
26 28
   private boolean misdirected;
27 29
   private boolean isUnloadedAtDestination;
28 30
   private RoutingStatus routingStatus;
29
-  private Date calculatedAt;
30 31
 
31 32
   /**
32 33
    * Derives a new delivery snapshot to reflect changes in routing, i.e.
@@ -40,10 +41,10 @@ public class Delivery implements ValueObject<Delivery> {
40 41
   Delivery withRoutingChange(final RouteSpecification routeSpecification, final Itinerary itinerary) {
41 42
     Validate.notNull(routeSpecification, "Route specification is required");
42 43
 
43
-    final RoutingStatus newRoutingStatus = RoutingStatus.derivedFrom(itinerary, routeSpecification);
44
+    final RoutingStatus newRoutingStatus = derivedFrom(itinerary, routeSpecification);
44 45
     boolean misdirected = false;
45 46
 
46
-    return new Delivery(transportStatus, lastKnownLocation, currentVoyage, misdirected, isUnloadedAtDestination, newRoutingStatus);
47
+    return new Delivery(mostRecentHandlingActivity, misdirected, isUnloadedAtDestination, newRoutingStatus);
47 48
   }
48 49
 
49 50
   /**
@@ -58,44 +59,33 @@ public class Delivery implements ValueObject<Delivery> {
58 59
     Validate.notNull(routeSpecification, "Route specification is required");
59 60
     Validate.notNull(itinerary, "Itinerary is required");
60 61
 
62
+    final boolean newMisdirected = misdirectionStatus(itinerary, handlingActivity);
63
+    final boolean newUnloadedAtDestination = unloadedAtDestination(routeSpecification, handlingActivity);
61 64
     final RoutingStatus newRoutingStatus = this.routingStatus;
62
-    final TransportStatus newTransportStatus = TransportStatus.derivedFrom(handlingActivity);
63
-    final boolean newMisdirected = Calculate.misdirectionStatus(itinerary, handlingActivity);
64
-    final Location newLastKnownLocation = Calculate.lastKnownLocation(handlingActivity);
65
-    final Voyage newCurrentVoyage = Calculate.currentVoyage(handlingActivity, newTransportStatus);
66
-    final boolean newUnloadedAtDestination = Calculate.unloadedAtDestination(routeSpecification, handlingActivity);
67 65
 
68
-    return new Delivery(newTransportStatus, newLastKnownLocation, newCurrentVoyage, newMisdirected, newUnloadedAtDestination, newRoutingStatus);
66
+    return new Delivery(handlingActivity, newMisdirected, newUnloadedAtDestination, newRoutingStatus);
69 67
   }
70 68
 
71 69
   /**
72
-   *
73
-   * @param routeSpecification
74
-   * @param itinerary
75
-   * @return
70
+   * @param routeSpecification route specification
71
+   * @param itinerary itinerary
72
+   * @return Initial delivery, before any handling has taken place
76 73
    */
77 74
   static Delivery initial(final RouteSpecification routeSpecification, final Itinerary itinerary) {
78 75
     Validate.notNull(routeSpecification, "Route specification is required");
79 76
 
80
-    final TransportStatus newTransportStatus = TransportStatus.derivedFrom(null);
81
-    final Location newLastKnownLocation = Calculate.lastKnownLocation(null);
82
-    final Voyage newCurrentVoyage = Calculate.currentVoyage(null, newTransportStatus);
83
-    final boolean newMisdirected = Calculate.misdirectionStatus(itinerary, null);
84
-    final boolean newUnloadedAtDestination = Calculate.unloadedAtDestination(routeSpecification, null);
85
-    final RoutingStatus newRoutingStatus = RoutingStatus.derivedFrom(itinerary, routeSpecification);
77
+    final boolean newMisdirected = misdirectionStatus(itinerary, null);
78
+    final boolean newUnloadedAtDestination = unloadedAtDestination(routeSpecification, null);
79
+    final RoutingStatus newRoutingStatus = derivedFrom(itinerary, routeSpecification);
86 80
 
87
-    return new Delivery(newTransportStatus, newLastKnownLocation, newCurrentVoyage, newMisdirected, newUnloadedAtDestination, newRoutingStatus);
81
+    return new Delivery(null, newMisdirected, newUnloadedAtDestination, newRoutingStatus);
88 82
   }
89 83
 
90
-  private Delivery(final TransportStatus transportStatus,
91
-                   final Location lastKnownLocation,
92
-                   final Voyage currentVoyage,
84
+  private Delivery(final HandlingActivity mostRecentHandlingActivity,
93 85
                    final boolean misdirected,
94 86
                    final boolean unloadedAtDestination,
95 87
                    final RoutingStatus routingStatus) {
96
-    this.transportStatus = transportStatus;
97
-    this.lastKnownLocation = lastKnownLocation;
98
-    this.currentVoyage = currentVoyage;
88
+    this.mostRecentHandlingActivity = mostRecentHandlingActivity;
99 89
     this.misdirected = misdirected;
100 90
     this.isUnloadedAtDestination = unloadedAtDestination;
101 91
     this.routingStatus = routingStatus;
@@ -106,21 +96,29 @@ public class Delivery implements ValueObject<Delivery> {
106 96
    * @return Transport status
107 97
    */
108 98
   public TransportStatus transportStatus() {
109
-    return transportStatus;
99
+    return TransportStatus.derivedFrom(mostRecentHandlingActivity);
110 100
   }
111 101
 
112 102
   /**
113 103
    * @return Last known location of the cargo, or Location.UNKNOWN if the delivery history is empty.
114 104
    */
115 105
   public Location lastKnownLocation() {
116
-    return DomainObjectUtils.nullSafe(lastKnownLocation, Location.UNKNOWN);
106
+    if (mostRecentHandlingActivity != null) {
107
+      return mostRecentHandlingActivity.location();
108
+    } else {
109
+      return Location.UNKNOWN;
110
+    }
117 111
   }
118 112
 
119 113
   /**
120 114
    * @return Current voyage.
121 115
    */
122 116
   public Voyage currentVoyage() {
123
-    return DomainObjectUtils.nullSafe(currentVoyage, Voyage.NONE);
117
+    if (mostRecentHandlingActivity != null && transportStatus().equals(ONBOARD_CARRIER)) {
118
+      return mostRecentHandlingActivity.voyage();
119
+    } else {
120
+      return Voyage.NONE;
121
+    }
124 122
   }
125 123
 
126 124
   /**
@@ -159,12 +157,30 @@ public class Delivery implements ValueObject<Delivery> {
159 157
     return new Date(calculatedAt.getTime());
160 158
   }
161 159
 
160
+  /**
161
+   * @return True if the cargo is routed and not misdirected
162
+   */
163
+  boolean onTrack() {
164
+    return routingStatus().sameValueAs(ROUTED) && !isMisdirected();
165
+  }
166
+
167
+  private static boolean misdirectionStatus(Itinerary itinerary, HandlingActivity handlingActivity) {
168
+    return handlingActivity != null &&
169
+           handlingActivity.type().isPhysical() &&
170
+           !itinerary.isExpected(handlingActivity);
171
+  }
172
+
173
+  // TODO name this: "arrived" or something
174
+  private static boolean unloadedAtDestination(RouteSpecification routeSpecification, HandlingActivity handlingActivity) {
175
+    return handlingActivity != null &&
176
+          (CLAIM.sameValueAs(handlingActivity.type()) || UNLOAD.sameValueAs(handlingActivity.type()) &&
177
+           routeSpecification.destination().sameIdentityAs(handlingActivity.location()));
178
+  }
179
+
162 180
   @Override
163 181
   public boolean sameValueAs(final Delivery other) {
164 182
     return other != null && new EqualsBuilder().
165
-      append(this.transportStatus, other.transportStatus).
166
-      append(this.lastKnownLocation, other.lastKnownLocation).
167
-      append(this.currentVoyage, other.currentVoyage).
183
+      append(this.mostRecentHandlingActivity, other.mostRecentHandlingActivity).
168 184
       append(this.misdirected, other.misdirected).
169 185
       append(this.isUnloadedAtDestination, other.isUnloadedAtDestination).
170 186
       append(this.routingStatus, other.routingStatus).
@@ -185,9 +201,7 @@ public class Delivery implements ValueObject<Delivery> {
185 201
   @Override
186 202
   public int hashCode() {
187 203
     return new HashCodeBuilder().
188
-      append(transportStatus).
189
-      append(lastKnownLocation).
190
-      append(currentVoyage).
204
+      append(mostRecentHandlingActivity).
191 205
       append(misdirected).
192 206
       append(isUnloadedAtDestination).
193 207
       append(routingStatus).
@@ -199,39 +213,4 @@ public class Delivery implements ValueObject<Delivery> {
199 213
     // Needed by Hibernate
200 214
   }
201 215
 
202
-  private static class Calculate {
203
-
204
-    private static Location lastKnownLocation(HandlingActivity handlingActivity) {
205
-      if (handlingActivity != null) {
206
-        return handlingActivity.location();
207
-      } else {
208
-        return null;
209
-      }
210
-    }
211
-
212
-    private static Voyage currentVoyage(HandlingActivity handlingActivity, TransportStatus transportStatus) {
213
-      if (transportStatus.equals(ONBOARD_CARRIER) && handlingActivity != null) {
214
-        return handlingActivity.voyage();
215
-      } else {
216
-        return null;
217
-      }
218
-    }
219
-
220
-    private static boolean misdirectionStatus(Itinerary itinerary, HandlingActivity handlingActivity) {
221
-      if (handlingActivity == null) {
222
-        return false;
223
-      } else {
224
-        return !itinerary.isExpected(handlingActivity);
225
-      }
226
-    }
227
-
228
-    private static boolean unloadedAtDestination(RouteSpecification routeSpecification, HandlingActivity handlingActivity) {
229
-      return handlingActivity != null &&
230
-        (HandlingEvent.Type.CLAIM.sameValueAs(handlingActivity.type()) ||
231
-          HandlingEvent.Type.UNLOAD.sameValueAs(handlingActivity.type()) &&
232
-            routeSpecification.destination().sameIdentityAs(handlingActivity.location()));
233
-    }
234
-
235
-  }
236
-
237 216
 }

+ 120
- 152
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/Projections.java Dosyayı Görüntüle

@@ -1,197 +1,165 @@
1 1
 package se.citerus.dddsample.domain.model.cargo;
2 2
 
3
-import se.citerus.dddsample.domain.shared.ValueObject;
4
-import se.citerus.dddsample.domain.model.shared.HandlingActivity;
5
-import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.ROUTED;
3
+import org.apache.commons.lang.builder.EqualsBuilder;
4
+import org.apache.commons.lang.builder.HashCodeBuilder;
5
+import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
6 6
 import se.citerus.dddsample.domain.model.location.Location;
7
-import se.citerus.dddsample.domain.model.voyage.Voyage;
8
-import se.citerus.dddsample.domain.model.handling.HandlingEvent;
7
+import se.citerus.dddsample.domain.model.shared.HandlingActivity;
8
+import se.citerus.dddsample.domain.shared.ValueObject;
9 9
 
10 10
 import java.util.Date;
11 11
 import java.util.Iterator;
12 12
 
13
-import org.apache.commons.lang.builder.EqualsBuilder;
14
-import org.apache.commons.lang.builder.HashCodeBuilder;
15
-
16 13
 /**
17 14
  * These are projections about the future handling of the cargo,
18 15
  * when it will arrive and what the next step is.
19
- *
16
+ * <p/>
20 17
  * It is updated on routing changes as well as handling.
21
- *
22 18
  */
23
-public class Projections implements ValueObject<Projections> {
19
+class Projections implements ValueObject<Projections> {
24 20
 
25
-    private Date estimatedTimeOfArrival;
26
-    private HandlingActivity nextExpectedActivity;
21
+  private Date estimatedTimeOfArrival;
22
+  private HandlingActivity nextExpectedActivity;
27 23
 
28
-    private static final Date ETA_UNKOWN = null;
29
-    private static final HandlingActivity NO_ACTIVITY = null;
24
+  private static final Date ETA_UNKOWN = null;
25
+  private static final HandlingActivity NO_ACTIVITY = null;
30 26
 
31
-    Projections(final Delivery delivery, final Itinerary itinerary, final RouteSpecification routeSpecification) {
32
-        this(delivery, itinerary, routeSpecification, null);
33
-    }
27
+  Projections(final Delivery delivery, final Itinerary itinerary) {
28
+    this.estimatedTimeOfArrival = calculateEstimatedTimeOfArrival(delivery, itinerary);
29
+    this.nextExpectedActivity = calculateNextExpectedActivity(delivery, itinerary);
30
+  }
34 31
 
35
-    Projections(final Delivery delivery, final Itinerary itinerary, final RouteSpecification routeSpecification, final HandlingActivity handlingActivity) {
36
-        this.estimatedTimeOfArrival = calculateEstimatedTimeOfArrival(delivery, itinerary);
37
-        this.nextExpectedActivity = calculateNextExpectedActivity(delivery, itinerary, routeSpecification, handlingActivity);
32
+  /**
33
+   * @return Estimated time of arrival, or null if not known.
34
+   */
35
+  Date estimatedTimeOfArrival() {
36
+    if (estimatedTimeOfArrival != ETA_UNKOWN) {
37
+      return new Date(estimatedTimeOfArrival.getTime());
38
+    } else {
39
+      return ETA_UNKOWN;
38 40
     }
41
+  }
39 42
 
40
-    Projections(final Date estimatedTimeOfArrival, final HandlingActivity nextExpectedActivity) {
41
-        this.estimatedTimeOfArrival = estimatedTimeOfArrival;
42
-        this.nextExpectedActivity = nextExpectedActivity;
43
-    }
43
+  /**
44
+   * @return The next expected handling activity.
45
+   */
46
+  HandlingActivity nextExpectedActivity() {
47
+    return nextExpectedActivity;
48
+  }
44 49
 
45
-    /**
46
-     * @return Estimated time of arrival, or null if not known.
47
-     */
48
-    public Date estimatedTimeOfArrival() {
49
-        if (estimatedTimeOfArrival != ETA_UNKOWN) {
50
-            return new Date(estimatedTimeOfArrival.getTime());
51
-        } else {
52
-            return ETA_UNKOWN;
53
-        }
50
+  private Date calculateEstimatedTimeOfArrival(final Delivery delivery, final Itinerary itinerary) {
51
+    if (delivery.onTrack()) {
52
+      return itinerary.finalUnloadTime();
53
+    } else {
54
+      return ETA_UNKOWN;
54 55
     }
56
+  }
55 57
 
56
-    /**
57
-     * @return The next expected handling activity.
58
-     */
59
-    public HandlingActivity nextExpectedActivity() {
60
-        return nextExpectedActivity;
61
-    }
58
+  private HandlingActivity calculateNextExpectedActivity(final Delivery delivery, final Itinerary itinerary) {
59
+
60
+    /*
61
+     Capture:
62 62
 
63
-    private Date calculateEstimatedTimeOfArrival(final Delivery delivery, final Itinerary itinerary) {
64
-        if (onTrack(delivery.routingStatus(), delivery.isMisdirected())) {
65
-            return itinerary.finalUnloadTime();
63
+     Cargo is misdirected but has been rerouted. Next expected acivity should be to load according to first leg
64
+     of new itinerary.
65
+
66
+     and
67
+
68
+     even if a cargo is misdirected, we expect it to be unloaded at next stop.
69
+
70
+    */
71
+    if (!delivery.onTrack()) return NO_ACTIVITY;
72
+
73
+    final Location lastKnownLocation = delivery.lastKnownLocation();
74
+    switch (delivery.transportStatus()) {
75
+      case IN_PORT:
76
+        if (itinerary.firstLeg().loadLocation().sameIdentityAs(lastKnownLocation)) {
77
+          return loadInFirstLocation(itinerary);
66 78
         } else {
67
-            return ETA_UNKOWN;
79
+          return loadOrClaimInNextLocation(itinerary, lastKnownLocation);
68 80
         }
69
-    }
70 81
 
71
-    private boolean onTrack(final RoutingStatus routingStatus, final boolean misdirected) {
72
-        return routingStatus.sameValueAs(ROUTED) && !misdirected;
73
-    }
82
+      case NOT_RECEIVED:
83
+        return receiveInFirstLocation(itinerary);
84
+
85
+      case ONBOARD_CARRIER:
86
+        return unloadInNextLocation(itinerary, lastKnownLocation);
74 87
 
75
-    private HandlingActivity calculateNextExpectedActivity(final Delivery delivery, final Itinerary itinerary, final RouteSpecification routeSpecification, final HandlingActivity handlingActivity) {
76
-        return calculateNextExpectedActivity(routeSpecification, itinerary, handlingActivity, delivery.routingStatus(), delivery.transportStatus(), delivery.lastKnownLocation(), delivery.currentVoyage(), delivery.isMisdirected());
88
+      case CLAIMED:
89
+      default:
90
+        return NO_ACTIVITY;
77 91
     }
92
+  }
78 93
 
79
-    private HandlingActivity calculateNextExpectedActivity(final RouteSpecification routeSpecification,
80
-                                                           final Itinerary itinerary,
81
-                                                           final HandlingActivity handlingActivity,
82
-                                                           final RoutingStatus routingStatus,
83
-                                                           final TransportStatus transportStatus,
84
-                                                           final Location lastKnownLocation,
85
-                                                           final Voyage currentVoyage,
86
-                                                           final boolean misdirected) {
87
-        /*
88
-         Capture:
89
-
90
-         Cargo is misdirected but has been rerouted. Next expected acivity should be to load according to first leg
91
-         of new itinerary.
92
-
93
-         and
94
-
95
-         even if a cargo is misdirected, we expect it to be unloaded at next stop.
96
-
97
-        */
98
-        if (!onTrack(routingStatus, misdirected)) return NO_ACTIVITY;
99
-
100
-        switch (transportStatus) {
101
-            case IN_PORT:
102
-                if (itinerary.firstLeg().loadLocation().sameIdentityAs(lastKnownLocation)) {
103
-                    return loadInFirstLocation(itinerary);
104
-                } else {
105
-                    return loadOrClaimInNextLocation(itinerary, lastKnownLocation);
106
-                }
107
-            case NOT_RECEIVED:
108
-                return receiveInFirstLocation(itinerary);
109
-            case ONBOARD_CARRIER:
110
-                return unloadInNextLocation(itinerary, lastKnownLocation);
111
-            case CLAIMED:
112
-            default:
113
-                return NO_ACTIVITY;
114
-        }
94
+  private HandlingActivity receiveInFirstLocation(final Itinerary itinerary) {
95
+    final Leg leg = itinerary.firstLeg();
96
+    return new HandlingActivity(RECEIVE, leg.loadLocation());
97
+  }
115 98
 
116
-        /*
117
-        switch (handlingActivity.type()) {
118
-          case LOAD:
119
-            return unloadInNextLocation(itinerary, handlingActivity);
120
-          case UNLOAD:
121
-            return loadOrClaimInNextLocation(itinerary, handlingActivity);
122
-          case RECEIVE:
123
-            return receiveInFirstLocation(itinerary);
124
-          case CLAIM:
125
-          default:
126
-            return NO_ACTIVITY;
127
-        }
128
-        */
129
-    }
99
+  private HandlingActivity loadInFirstLocation(final Itinerary itinerary) {
100
+    final Leg leg = itinerary.firstLeg();
101
+    return new HandlingActivity(LOAD, leg.loadLocation(), leg.voyage());
102
+  }
130 103
 
131
-    private HandlingActivity receiveInFirstLocation(final Itinerary itinerary) {
132
-        final Leg leg = itinerary.firstLeg();
133
-        return new HandlingActivity(HandlingEvent.Type.RECEIVE, leg.loadLocation());
134
-    }
104
+  private HandlingActivity loadOrClaimInNextLocation(final Itinerary itinerary, final Location activityLocation) {
105
+    for (final Iterator<Leg> it = itinerary.legs().iterator(); it.hasNext();) {
106
+      final Leg leg = it.next();
107
+      if (leg.unloadLocation().sameIdentityAs(activityLocation)) {
108
+        if (it.hasNext()) {
109
+          final Leg nextLeg = it.next();
135 110
 
136
-    private HandlingActivity loadInFirstLocation(final Itinerary itinerary) {
137
-        final Leg leg = itinerary.firstLeg();
138
-        return new HandlingActivity(HandlingEvent.Type.LOAD, leg.loadLocation(), leg.voyage());
139
-    }
111
+          //return leg.loadActivity(); { return new HandlingActivity(voyage, loadLocation); }
140 112
 
141
-    private HandlingActivity loadOrClaimInNextLocation(final Itinerary itinerary, final Location activityLocation) {
142
-        for (final Iterator<Leg> it = itinerary.legs().iterator(); it.hasNext();) {
143
-            final Leg leg = it.next();
144
-            if (leg.unloadLocation().sameIdentityAs(activityLocation)) {
145
-                if (it.hasNext()) {
146
-                    final Leg nextLeg = it.next();
147
-                    return new HandlingActivity(HandlingEvent.Type.LOAD, nextLeg.loadLocation(), nextLeg.voyage());
148
-                } else {
149
-                    return new HandlingActivity(HandlingEvent.Type.CLAIM, leg.unloadLocation());
150
-                }
151
-            }
152
-        }
113
+          //return HandlingActivity.loadOnto(nextLeg.voyage()).in(nextLeg.loadLocation());
153 114
 
154
-        return NO_ACTIVITY;
115
+          return new HandlingActivity(LOAD, nextLeg.loadLocation(), nextLeg.voyage());
116
+        } else {
117
+          return new HandlingActivity(CLAIM, leg.unloadLocation());
118
+        }
119
+      }
155 120
     }
156 121
 
157
-    private HandlingActivity unloadInNextLocation(final Itinerary itinerary, final Location activityLocation) {
158
-        for (final Leg leg : itinerary.legs()) {
159
-            if (leg.loadLocation().sameIdentityAs(activityLocation)) {
160
-                return new HandlingActivity(HandlingEvent.Type.UNLOAD, leg.unloadLocation(), leg.voyage());
161
-            }
162
-        }
122
+    return NO_ACTIVITY;
123
+  }
163 124
 
164
-        return NO_ACTIVITY;
125
+  private HandlingActivity unloadInNextLocation(final Itinerary itinerary, final Location activityLocation) {
126
+    for (final Leg leg : itinerary.legs()) {
127
+      if (leg.loadLocation().sameIdentityAs(activityLocation)) {
128
+        return new HandlingActivity(UNLOAD, leg.unloadLocation(), leg.voyage());
129
+      }
165 130
     }
166 131
 
132
+    return NO_ACTIVITY;
133
+  }
167 134
 
168
-    @Override
169
-    public boolean sameValueAs(final Projections other) {
170
-        return other != null && new EqualsBuilder().
171
-                append(this.estimatedTimeOfArrival, other.estimatedTimeOfArrival).
172
-                append(this.nextExpectedActivity, other.nextExpectedActivity).
173
-                isEquals();
174
-    }
175 135
 
176
-    @Override
177
-    public boolean equals(final Object o) {
178
-        if (this == o) return true;
179
-        if (o == null || getClass() != o.getClass()) return false;
136
+  @Override
137
+  public boolean sameValueAs(final Projections other) {
138
+    return other != null && new EqualsBuilder().
139
+      append(this.estimatedTimeOfArrival, other.estimatedTimeOfArrival).
140
+      append(this.nextExpectedActivity, other.nextExpectedActivity).
141
+      isEquals();
142
+  }
180 143
 
181
-        final Projections other = (Projections) o;
182
-        return sameValueAs(other);
183
-    }
144
+  @Override
145
+  public boolean equals(final Object o) {
146
+    if (this == o) return true;
147
+    if (o == null || getClass() != o.getClass()) return false;
184 148
 
185
-    @Override
186
-    public int hashCode() {
187
-        return new HashCodeBuilder().
188
-                append(estimatedTimeOfArrival).
189
-                append(nextExpectedActivity).
190
-                toHashCode();
191
-    }
149
+    final Projections other = (Projections) o;
150
+    return sameValueAs(other);
151
+  }
192 152
 
193
-    Projections() {
194
-        // Needed by Hibernate
195
-    }
153
+  @Override
154
+  public int hashCode() {
155
+    return new HashCodeBuilder().
156
+      append(estimatedTimeOfArrival).
157
+      append(nextExpectedActivity).
158
+      toHashCode();
159
+  }
160
+
161
+  Projections() {
162
+    // Needed by Hibernate
163
+  }
196 164
 
197 165
 }

+ 6
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/TrackingId.java Dosyayı Görüntüle

@@ -1,5 +1,6 @@
1 1
 package se.citerus.dddsample.domain.model.cargo;
2 2
 
3
+import org.apache.commons.lang.StringUtils;
3 4
 import org.apache.commons.lang.Validate;
4 5
 import se.citerus.dddsample.domain.shared.ValueObject;
5 6
 
@@ -22,6 +23,11 @@ public final class TrackingId implements ValueObject<TrackingId>, Serializable {
22 23
     this.id = id;
23 24
   }
24 25
 
26
+  public TrackingId(final long sequenceValue) {
27
+    Validate.isTrue(sequenceValue > 0, "Sequence value must be larger than 0");
28
+    this.id = "C" + StringUtils.leftPad(String.valueOf(sequenceValue), 8, "0");
29
+  }
30
+
25 31
   /**
26 32
    * @return String representation of this tracking id.
27 33
    */

+ 10
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/TrackingIdGenerator.java Dosyayı Görüntüle

@@ -0,0 +1,10 @@
1
+package se.citerus.dddsample.domain.model.cargo;
2
+
3
+/**
4
+ * Generates tracking ids for cargos. This is a domain service.
5
+ */
6
+public interface TrackingIdGenerator {
7
+
8
+  TrackingId nextTrackingId();
9
+
10
+}

+ 37
- 19
dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEvent.java Dosyayı Görüntüle

@@ -5,6 +5,7 @@ import org.apache.commons.lang.builder.EqualsBuilder;
5 5
 import org.apache.commons.lang.builder.HashCodeBuilder;
6 6
 import se.citerus.dddsample.domain.model.cargo.Cargo;
7 7
 import se.citerus.dddsample.domain.model.location.Location;
8
+import se.citerus.dddsample.domain.model.shared.EventSequenceNumber;
8 9
 import se.citerus.dddsample.domain.model.shared.HandlingActivity;
9 10
 import se.citerus.dddsample.domain.model.voyage.Voyage;
10 11
 import se.citerus.dddsample.domain.shared.DomainEvent;
@@ -31,7 +32,8 @@ import java.util.Date;
31 32
  */
32 33
 public final class HandlingEvent implements DomainEvent<HandlingEvent> {
33 34
 
34
-  private HandlingActivity handlingActivity;
35
+  private EventSequenceNumber sequenceNumber;
36
+  private HandlingActivity activity;
35 37
   private Date completionTime;
36 38
   private Date registrationTime;
37 39
   private Cargo cargo;
@@ -41,21 +43,24 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
41 43
    * association, it's never optional.
42 44
    */
43 45
   public enum Type implements ValueObject<Type> {
44
-    LOAD(true),
45
-    UNLOAD(true),
46
-    RECEIVE(false),
47
-    CLAIM(false),
48
-    CUSTOMS(false);
46
+    LOAD(true, true),
47
+    UNLOAD(true, true),
48
+    RECEIVE(false, true),
49
+    CLAIM(false, true),
50
+    CUSTOMS(false, false);
49 51
 
50 52
     private final boolean voyageRequired;
53
+    private final boolean physical;
51 54
 
52 55
     /**
53 56
      * Private enum constructor.
54 57
      *
55 58
      * @param voyageRequired whether or not a voyage is associated with this event type
59
+     * @param physical whether or not this event type is physical
56 60
      */
57
-    private Type(final boolean voyageRequired) {
61
+    private Type(final boolean voyageRequired, boolean physical) {
58 62
       this.voyageRequired = voyageRequired;
63
+      this.physical = physical;
59 64
     }
60 65
 
61 66
     /**
@@ -72,6 +77,13 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
72 77
       return !requiresVoyage();
73 78
     }
74 79
 
80
+    /**
81
+     * @return True if this is a physical handling.
82
+     */
83
+    public boolean isPhysical() {
84
+      return physical;
85
+    }
86
+
75 87
     @Override
76 88
     public boolean sameValueAs(Type other) {
77 89
       return other != null && this.equals(other);
@@ -104,10 +116,11 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
104 116
       throw new IllegalArgumentException("Voyage is not allowed with event type " + type);
105 117
     }
106 118
 
107
-    this.completionTime = (Date) completionTime.clone();
108
-    this.registrationTime = (Date) registrationTime.clone();
119
+    this.sequenceNumber = EventSequenceNumber.next();
120
+    this.completionTime = new Date(completionTime.getTime());
121
+    this.registrationTime = new Date(registrationTime.getTime());
109 122
     this.cargo = cargo;
110
-    this.handlingActivity = new HandlingActivity(type, location, voyage);
123
+    this.activity = new HandlingActivity(type, location, voyage);
111 124
   }
112 125
 
113 126
   /**
@@ -133,22 +146,27 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
133 146
       throw new IllegalArgumentException("Voyage is required for event type " + type);
134 147
     }
135 148
 
149
+    this.sequenceNumber = EventSequenceNumber.next();
136 150
     this.completionTime = new Date(completionTime.getTime());
137 151
     this.registrationTime = new Date(registrationTime.getTime());
138 152
     this.cargo = cargo;
139
-    this.handlingActivity = new HandlingActivity(type, location);
153
+    this.activity = new HandlingActivity(type, location);
154
+  }
155
+
156
+  public EventSequenceNumber sequenceNumber() {
157
+    return sequenceNumber;
140 158
   }
141 159
 
142
-  public HandlingActivity handlingActivity() {
143
-    return handlingActivity;
160
+  public HandlingActivity activity() {
161
+    return activity;
144 162
   }
145 163
 
146 164
   public Type type() {
147
-    return handlingActivity.type();
165
+    return activity.type();
148 166
   }
149 167
 
150 168
   public Voyage voyage() {
151
-    return DomainObjectUtils.nullSafe(handlingActivity.voyage(), Voyage.NONE);
169
+    return DomainObjectUtils.nullSafe(activity.voyage(), Voyage.NONE);
152 170
   }
153 171
 
154 172
   public Date completionTime() {
@@ -160,7 +178,7 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
160 178
   }
161 179
 
162 180
   public Location location() {
163
-    return handlingActivity.location();
181
+    return activity.location();
164 182
   }
165 183
 
166 184
   public Cargo cargo() {
@@ -182,7 +200,7 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
182 200
     return other != null && new EqualsBuilder().
183 201
       append(this.cargo, other.cargo).
184 202
       append(this.completionTime, other.completionTime).
185
-      append(this.handlingActivity, other.handlingActivity).
203
+      append(this.activity, other.activity).
186 204
       isEquals();
187 205
   }
188 206
 
@@ -191,14 +209,14 @@ public final class HandlingEvent implements DomainEvent<HandlingEvent> {
191 209
     return new HashCodeBuilder().
192 210
       append(cargo).
193 211
       append(completionTime).
194
-      append(handlingActivity).
212
+      append(activity).
195 213
       toHashCode();
196 214
   }
197 215
 
198 216
   @Override
199 217
   public String toString() {
200 218
     return "Cargo: " + cargo +
201
-      "\nActivity: " + handlingActivity +
219
+      "\nActivity: " + activity +
202 220
       "\nCompleted on: " + completionTime +
203 221
       "\nRegistered on: " + registrationTime;
204 222
   }

+ 7
- 2
dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEventRepository.java Dosyayı Görüntüle

@@ -1,6 +1,7 @@
1 1
 package se.citerus.dddsample.domain.model.handling;
2 2
 
3 3
 import se.citerus.dddsample.domain.model.cargo.Cargo;
4
+import se.citerus.dddsample.domain.model.shared.EventSequenceNumber;
4 5
 
5 6
 /**
6 7
  * Handling event repository.
@@ -8,13 +9,18 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
8 9
 public interface HandlingEventRepository {
9 10
 
10 11
   /**
12
+   * @param eventSequenceNumber event sequence number
13
+   * @return The handling event with this sequence number, or null if not found
14
+   */
15
+  HandlingEvent find(EventSequenceNumber eventSequenceNumber);
16
+
17
+  /**
11 18
    * Stores a (new) handling event.
12 19
    *
13 20
    * @param event handling event to save
14 21
    */
15 22
   void store(HandlingEvent event);
16 23
 
17
-
18 24
   /**
19 25
    * @param cargo cargo
20 26
    * @return The handling history of this cargo
@@ -27,5 +33,4 @@ public interface HandlingEventRepository {
27 33
    * @return The most recent handling of the cargo, or null if it has never been handled.
28 34
    */
29 35
   HandlingEvent mostRecentHandling(Cargo cargo);
30
-
31 36
 }

+ 51
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/model/shared/EventSequenceNumber.java Dosyayı Görüntüle

@@ -0,0 +1,51 @@
1
+package se.citerus.dddsample.domain.model.shared;
2
+
3
+import se.citerus.dddsample.domain.shared.ValueObject;
4
+
5
+import java.util.concurrent.atomic.AtomicLong;
6
+
7
+public class EventSequenceNumber implements ValueObject<EventSequenceNumber> {
8
+
9
+  private long value;
10
+  private static final AtomicLong SEQUENCE = new AtomicLong(System.currentTimeMillis());
11
+
12
+  private EventSequenceNumber(final long value) {
13
+    this.value = value;
14
+  }
15
+
16
+  public static EventSequenceNumber next() {
17
+    return new EventSequenceNumber(SEQUENCE.getAndIncrement());
18
+  }
19
+
20
+  public long longValue() {
21
+    return value;
22
+  }
23
+
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() {
44
+    return String.valueOf(value);
45
+  }
46
+
47
+  EventSequenceNumber() {
48
+    // Needed by Hibernate
49
+  }
50
+
51
+}

+ 6
- 6
dddsample/src/main/java/se/citerus/dddsample/infrastructure/messaging/jms/JmsApplicationEventsImpl.java Dosyayı Görüntüle

@@ -18,10 +18,10 @@ public final class JmsApplicationEventsImpl implements ApplicationEvents {
18 18
 
19 19
   private JmsOperations jmsOperations;
20 20
   private Destination cargoHandledDestination;
21
-  private Destination cargoDeliveryUpdateDestination;
21
+  private Destination cargoUpdateDestination;
22 22
 
23 23
   @Override
24
-  public void cargoWasHandled(final HandlingEvent event) {
24
+  public void notifyOfHandlingEvent(final HandlingEvent event) {
25 25
     final Cargo cargo = event.cargo();
26 26
     jmsOperations.send(cargoHandledDestination, new MessageCreator() {
27 27
       public Message createMessage(final Session session) throws JMSException {
@@ -31,8 +31,8 @@ public final class JmsApplicationEventsImpl implements ApplicationEvents {
31 31
   }
32 32
 
33 33
   @Override
34
-  public void cargoDeliveryWasUpdated(final Cargo cargo) {
35
-    jmsOperations.send(cargoDeliveryUpdateDestination, new MessageCreator() {
34
+  public void notifyOfCargoUpdate(final Cargo cargo) {
35
+    jmsOperations.send(cargoUpdateDestination, new MessageCreator() {
36 36
       public Message createMessage(Session session) throws JMSException {
37 37
         return session.createObjectMessage(cargo.trackingId());
38 38
       }
@@ -47,8 +47,8 @@ public final class JmsApplicationEventsImpl implements ApplicationEvents {
47 47
     this.cargoHandledDestination = cargoHandledDestination;
48 48
   }
49 49
 
50
-  public void setCargoDeliveryUpdateDestination(Destination cargoDeliveryUpdateDestination) {
51
-    this.cargoDeliveryUpdateDestination = cargoDeliveryUpdateDestination;
50
+  public void setCargoUpdateDestination(Destination cargoUpdateDestination) {
51
+    this.cargoUpdateDestination = cargoUpdateDestination;
52 52
   }
53 53
 
54 54
 }

+ 32
- 0
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/DatabaseTrackingIdGenerator.java Dosyayı Görüntüle

@@ -0,0 +1,32 @@
1
+/**
2
+ * Purpose
3
+ * @author peter
4
+ * @created 2009-sep-17
5
+ * $Id$
6
+ */
7
+package se.citerus.dddsample.infrastructure.persistence;
8
+
9
+import org.hibernate.SessionFactory;
10
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
11
+import se.citerus.dddsample.domain.model.cargo.TrackingIdGenerator;
12
+
13
+public class DatabaseTrackingIdGenerator implements TrackingIdGenerator {
14
+
15
+  private final SessionFactory sessionFactory;
16
+  private static final String SEQUENCE_NAME = "TRACKING_ID_SEQ";
17
+
18
+  public DatabaseTrackingIdGenerator(final SessionFactory sessionFactory) {
19
+    this.sessionFactory = sessionFactory;
20
+  }
21
+
22
+  @Override
23
+  public TrackingId nextTrackingId() {
24
+    final Long seq = (Long) sessionFactory.getCurrentSession().
25
+      createSQLQuery("select next_value from system_sequences where sequence_name = ?").
26
+      setParameter(1, SEQUENCE_NAME).
27
+      uniqueResult();
28
+
29
+    return new TrackingId(seq);
30
+  }
31
+
32
+}

+ 23
- 0
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/TrackingIdGeneratorInMem.java Dosyayı Görüntüle

@@ -0,0 +1,23 @@
1
+/**
2
+ * Purpose
3
+ * @author peter
4
+ * @created 2009-sep-07
5
+ * $Id$
6
+ */
7
+package se.citerus.dddsample.infrastructure.persistence;
8
+
9
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
10
+import se.citerus.dddsample.domain.model.cargo.TrackingIdGenerator;
11
+
12
+import java.util.concurrent.atomic.AtomicLong;
13
+
14
+public class TrackingIdGeneratorInMem implements TrackingIdGenerator {
15
+
16
+  private static final AtomicLong SEQ = new AtomicLong(1);
17
+
18
+  @Override
19
+  public TrackingId nextTrackingId() {
20
+    return new TrackingId(SEQ.getAndIncrement());
21
+  }
22
+
23
+}

+ 3
- 9
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.java Dosyayı Görüntüle

@@ -7,7 +7,6 @@ import se.citerus.dddsample.domain.model.cargo.TrackingId;
7 7
 import se.citerus.dddsample.domain.model.voyage.Voyage;
8 8
 
9 9
 import java.util.List;
10
-import java.util.UUID;
11 10
 
12 11
 /**
13 12
  * Hibernate implementation of CargoRepository.
@@ -15,6 +14,7 @@ import java.util.UUID;
15 14
 @Repository
16 15
 public class CargoRepositoryHibernate extends HibernateRepository implements CargoRepository {
17 16
 
17
+  @Override
18 18
   public Cargo find(TrackingId tid) {
19 19
     return (Cargo) getSession().
20 20
       createQuery("from Cargo where trackingId = :tid").
@@ -33,21 +33,15 @@ public class CargoRepositoryHibernate extends HibernateRepository implements Car
33 33
       list();
34 34
   }
35 35
 
36
+  @Override
36 37
   public void store(Cargo cargo) {
37 38
     getSession().saveOrUpdate(cargo);
38 39
     // Delete-orphan does not seem to work correctly when the parent is a component
39 40
     getSession().createSQLQuery("delete from Leg where cargo_id = null").executeUpdate();
40 41
   }
41 42
 
42
-  public TrackingId nextTrackingId() {
43
-    // TODO use an actual DB sequence here, UUID is for in-mem
44
-    final String random = UUID.randomUUID().toString().toUpperCase();
45
-    return new TrackingId(
46
-      random.substring(0, random.indexOf("-"))
47
-    );
48
-  }
49
-
50 43
   @SuppressWarnings("unchecked")
44
+  @Override
51 45
   public List<Cargo> findAll() {
52 46
     return getSession().createQuery("from Cargo").list();
53 47
   }

+ 13
- 4
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryHibernate.java Dosyayı Görüntüle

@@ -5,6 +5,7 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
5 5
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
6 6
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
7 7
 import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8
+import se.citerus.dddsample.domain.model.shared.EventSequenceNumber;
8 9
 
9 10
 import java.util.List;
10 11
 
@@ -15,14 +16,22 @@ import java.util.List;
15 16
 public class HandlingEventRepositoryHibernate extends HibernateRepository implements HandlingEventRepository {
16 17
 
17 18
   @Override
19
+  public HandlingEvent find(EventSequenceNumber eventSequenceNumber) {
20
+    return (HandlingEvent) getSession().
21
+      createQuery("from HandlingEvent where sequenceNumber = :sn").
22
+      setParameter("sn", eventSequenceNumber).
23
+      uniqueResult();
24
+  }
25
+
26
+  @Override
18 27
   public void store(final HandlingEvent event) {
19 28
     getSession().save(event);
20 29
   }
21 30
 
22 31
   @Override
23 32
   public HandlingHistory lookupHandlingHistoryOfCargo(final Cargo cargo) {
24
-    final List handlingEvents = getSession().createQuery(
25
-      "from HandlingEvent where cargo.trackingId = :tid").
33
+    final List handlingEvents = getSession().
34
+      createQuery("from HandlingEvent where cargo.trackingId = :tid").
26 35
       setParameter("tid", cargo.trackingId()).
27 36
       list();
28 37
 
@@ -35,8 +44,8 @@ public class HandlingEventRepositoryHibernate extends HibernateRepository implem
35 44
 
36 45
   @Override
37 46
   public HandlingEvent mostRecentHandling(Cargo cargo) {
38
-      return (HandlingEvent) getSession().createQuery(
39
-        "from HandlingEvent where cargo = :cargo order by completionTime desc").
47
+      return (HandlingEvent) getSession().
48
+        createQuery("from HandlingEvent where cargo = :cargo order by completionTime desc").
40 49
         setParameter("cargo", cargo).
41 50
         setMaxResults(1).
42 51
         uniqueResult();

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/interfaces/booking/facade/internal/assembler/CargoRoutingDTOAssembler.java Dosyayı Görüntüle

@@ -20,7 +20,7 @@ public class CargoRoutingDTOAssembler {
20 20
       cargo.routeSpecification().origin().unLocode().stringValue(),
21 21
       cargo.routeSpecification().destination().unLocode().stringValue(),
22 22
       cargo.routeSpecification().arrivalDeadline(),
23
-      cargo.delivery().routingStatus().sameValueAs(RoutingStatus.MISROUTED));
23
+      cargo.routingStatus().sameValueAs(RoutingStatus.MISROUTED));
24 24
     for (Leg leg : cargo.itinerary().legs()) {
25 25
       dto.addLeg(
26 26
         leg.voyage().voyageNumber().stringValue(),

+ 8
- 10
dddsample/src/main/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapter.java Dosyayı Görüntüle

@@ -2,7 +2,6 @@ package se.citerus.dddsample.interfaces.tracking;
2 2
 
3 3
 import org.springframework.context.MessageSource;
4 4
 import se.citerus.dddsample.domain.model.cargo.Cargo;
5
-import se.citerus.dddsample.domain.model.cargo.Delivery;
6 5
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
7 6
 import se.citerus.dddsample.domain.model.location.Location;
8 7
 import se.citerus.dddsample.domain.model.shared.HandlingActivity;
@@ -60,16 +59,15 @@ public final class CargoTrackingViewAdapter {
60 59
    * @return A translated string describing the cargo status.
61 60
    */
62 61
   public String getStatusText() {
63
-    final Delivery delivery = cargo.delivery();
64
-    final String code = "cargo.status." + delivery.transportStatus().name();
62
+    final String code = "cargo.status." + cargo.transportStatus().name();
65 63
 
66 64
     final Object[] args;
67
-    switch (delivery.transportStatus()) {
65
+    switch (cargo.transportStatus()) {
68 66
       case IN_PORT:
69
-        args = new Object[]{getDisplayText(delivery.lastKnownLocation())};
67
+        args = new Object[]{getDisplayText(cargo.lastKnownLocation())};
70 68
         break;
71 69
       case ONBOARD_CARRIER:
72
-        args = new Object[]{delivery.currentVoyage().voyageNumber().stringValue()};
70
+        args = new Object[]{cargo.currentVoyage().voyageNumber().stringValue()};
73 71
         break;
74 72
       case CLAIMED:
75 73
       case NOT_RECEIVED:
@@ -104,7 +102,7 @@ public final class CargoTrackingViewAdapter {
104 102
   }
105 103
 
106 104
   public String getEta() {
107
-    Date eta = cargo.projections().estimatedTimeOfArrival();
105
+    Date eta = cargo.estimatedTimeOfArrival();
108 106
 
109 107
     if (eta == null) return "?";
110 108
     else {
@@ -116,7 +114,7 @@ public final class CargoTrackingViewAdapter {
116 114
   }
117 115
 
118 116
   public String getNextExpectedActivity() {
119
-    HandlingActivity activity = cargo.projections().nextExpectedActivity();
117
+    HandlingActivity activity = cargo.nextExpectedActivity();
120 118
     if (activity == null) {
121 119
       return "";
122 120
     }
@@ -140,7 +138,7 @@ public final class CargoTrackingViewAdapter {
140 138
    * @return True if cargo is misdirected.
141 139
    */
142 140
   public boolean isMisdirected() {
143
-    return cargo.delivery().isMisdirected();
141
+    return cargo.isMisdirected();
144 142
   }
145 143
 
146 144
   /**
@@ -195,7 +193,7 @@ public final class CargoTrackingViewAdapter {
195 193
      * @return True if the event was expected, according to the cargo's itinerary.
196 194
      */
197 195
     public boolean isExpected() {
198
-      return cargo.itinerary().isExpected(handlingEvent.handlingActivity());
196
+      return cargo.itinerary().isExpected(handlingEvent.activity());
199 197
     }
200 198
 
201 199
     public String getDescription() {

+ 1
- 1
dddsample/src/main/resources/context-application.xml Dosyayı Görüntüle

@@ -17,7 +17,7 @@
17 17
     <constructor-arg ref="locationRepository"/>
18 18
   </bean>
19 19
 
20
-  <bean id="cargoDeliveryUpdater" class="se.citerus.dddsample.application.event.CargoDeliveryUpdater">
20
+  <bean id="cargoUpdater" class="se.citerus.dddsample.application.event.CargoUpdater">
21 21
     <constructor-arg ref="cargoRepository"/>
22 22
     <constructor-arg ref="applicationEvents"/>
23 23
     <constructor-arg ref="handlingEventRepository"/>

+ 1
- 1
dddsample/src/main/resources/context-domain.xml Dosyayı Görüntüle

@@ -11,7 +11,7 @@
11 11
   </bean>
12 12
 
13 13
   <bean id="cargoFactory" class="se.citerus.dddsample.domain.model.cargo.CargoFactory">
14
-    <constructor-arg ref="cargoRepository"/>
14
+    <constructor-arg ref="trackingIdGenerator"/>
15 15
     <constructor-arg ref="locationRepository"/>
16 16
   </bean>
17 17
 

+ 6
- 6
dddsample/src/main/resources/context-infrastructure-messaging.xml Dosyayı Görüntüle

@@ -14,12 +14,12 @@
14 14
   <amq:connectionFactory id="jmsConnectionFactory" brokerURL="vm://localhost?broker.persistent=false&amp;broker.useJmx=false"/>
15 15
 
16 16
   <amq:queue id="cargoHandledQueue" name="CargoHandledQueue" physicalName="CargoHandledQueue"/>
17
-  <amq:topic id="cargoDeliveryUpdateTopic" name="CargoDeliveryUpdateTopic" physicalName="CargoDeliveryUpdateTopic"/>
17
+  <amq:topic id="cargoUpdateTopic" name="CargUpdateTopic" physicalName="CargoUpdateTopic"/>
18 18
 
19 19
   <jms:listener-container connection-factory="jmsConnectionFactory">
20 20
     <jms:listener destination="CargoHandledQueue" ref="listener3"/>
21
-    <jms:listener destination="CargoDeliveryUpdateTopic" ref="listener1"/>
22
-    <jms:listener destination="CargoDeliveryUpdateTopic" ref="listener2"/>
21
+    <jms:listener destination="CargoUpdateTopic" ref="listener1"/>
22
+    <jms:listener destination="CargoUpdateTopic" ref="listener2"/>
23 23
   </jms:listener-container>
24 24
 
25 25
   <bean id="jmsOperations" class="org.springframework.jms.core.JmsTemplate">
@@ -29,7 +29,7 @@
29 29
   <bean id="applicationEvents" class="se.citerus.dddsample.infrastructure.messaging.jms.JmsApplicationEventsImpl">
30 30
     <property name="jmsOperations" ref="jmsOperations"/>
31 31
     <property name="cargoHandledDestination" ref="cargoHandledQueue"/>
32
-    <property name="cargoDeliveryUpdateDestination" ref="cargoDeliveryUpdateTopic"/>
32
+    <property name="cargoUpdateDestination" ref="cargoUpdateTopic"/>
33 33
   </bean>
34 34
 
35 35
   <bean id="listener1" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
@@ -43,8 +43,8 @@
43 43
   </bean>
44 44
 
45 45
   <bean id="listener3" class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
46
-    <constructor-arg ref="cargoDeliveryUpdater"/>
47
-    <property name="defaultListenerMethod" value="updateDelivery"/>
46
+    <constructor-arg ref="cargoUpdater"/>
47
+    <property name="defaultListenerMethod" value="updateCargo"/>
48 48
   </bean>
49 49
 
50 50
 </beans>

+ 2
- 0
dddsample/src/main/resources/context-infrastructure-persistence.xml Dosyayı Görüntüle

@@ -52,4 +52,6 @@
52 52
     <property name="sessionFactory" ref="sessionFactory"/>
53 53
   </bean>
54 54
 
55
+  <bean id="trackingIdGenerator" class="se.citerus.dddsample.infrastructure.persistence.TrackingIdGeneratorInMem"/>
56
+  
55 57
 </beans>

+ 12
- 10
dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/Cargo.hbm.xml Dosyayı Görüntüle

@@ -30,25 +30,27 @@
30 30
     </component>
31 31
 
32 32
     <component name="delivery" lazy="true">
33
-      <property name="misdirected" column="is_misdirected" not-null="true"/>
34 33
       <property name="calculatedAt" column="calculated_at" not-null="true"/>
35
-      <property name="isUnloadedAtDestination" column="unloaded_at_dest" not-null="true"/>
36 34
 
35
+      <property name="misdirected" column="is_misdirected" not-null="true"/>
37 36
       <property name="routingStatus" column="routing_status" not-null="true">
38 37
         <type name="org.hibernate.type.EnumType">
39 38
           <param name="enumClass">se.citerus.dddsample.domain.model.cargo.RoutingStatus</param>
40 39
           <param name="type">12</param><!-- 12 is java.sql.Types.VARCHAR -->
41 40
         </type>
42 41
       </property>
42
+      <property name="isUnloadedAtDestination" column="unloaded_at_dest" not-null="true"/>
43 43
 
44
-      <property name="transportStatus" column="transport_status" not-null="true">
45
-        <type name="org.hibernate.type.EnumType">
46
-          <param name="enumClass">se.citerus.dddsample.domain.model.cargo.TransportStatus</param>
47
-          <param name="type">12</param><!-- 12 is java.sql.Types.VARCHAR -->
48
-        </type>
49
-      </property>
50
-      <many-to-one name="currentVoyage" column="current_voyage_id" not-null="false" cascade="none" foreign-key="current_voyage_fk"/>
51
-      <many-to-one name="lastKnownLocation" column="last_known_location_id" not-null="false" cascade="none" foreign-key="last_known_location_fk"/>
44
+      <component name="mostRecentHandlingActivity">
45
+        <many-to-one name="location" column="most_recent_location_id" foreign-key="most_recent_location_fk" cascade="none"/>
46
+        <property name="type" column="most_recent_handling_event_type">
47
+          <type name="org.hibernate.type.EnumType">
48
+            <param name="enumClass">se.citerus.dddsample.domain.model.handling.HandlingEvent$Type</param>
49
+            <param name="type">12</param><!-- 12 is java.sql.Types.VARCHAR -->
50
+          </type>
51
+        </property>
52
+        <many-to-one name="voyage" column="most_recent_voyage_id" foreign-key="most_recent_voyage_fk" cascade="none"/>
53
+      </component>
52 54
     </component>
53 55
 
54 56
     <component name="routeSpecification">

+ 4
- 1
dddsample/src/main/resources/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEvent.hbm.xml Dosyayı Görüntüle

@@ -9,10 +9,13 @@
9 9
     <id name="id" column="id">
10 10
       <generator class="org.hibernate.id.IdentityGenerator"/>
11 11
     </id>
12
+    <component name="sequenceNumber" update="false">
13
+      <property name="value" column="sequence_number" not-null="true"/>
14
+    </component>
12 15
     <many-to-one name="cargo" column="cargo_id" not-null="true" cascade="none" foreign-key="cargo_fk"/>
13 16
     <property name="completionTime" column="completionTime" not-null="true"/>
14 17
     <property name="registrationTime" column="registrationTime" not-null="true"/>
15
-    <component name="handlingActivity">
18
+    <component name="activity">
16 19
       <many-to-one name="voyage" column="voyage_id" not-null="false" cascade="none" foreign-key="event_voyage_fk"/>
17 20
       <many-to-one name="location" column="location_id" not-null="true" cascade="none" foreign-key="location_fk"/>
18 21
       <property name="type" column="type" not-null="true">

+ 8
- 8
dddsample/src/test/java/se/citerus/dddsample/application/BookingServiceTest.java Dosyayı Görüntüle

@@ -3,10 +3,7 @@ package se.citerus.dddsample.application;
3 3
 import junit.framework.TestCase;
4 4
 import static org.easymock.EasyMock.*;
5 5
 import se.citerus.dddsample.application.impl.BookingServiceImpl;
6
-import se.citerus.dddsample.domain.model.cargo.Cargo;
7
-import se.citerus.dddsample.domain.model.cargo.CargoFactory;
8
-import se.citerus.dddsample.domain.model.cargo.CargoRepository;
9
-import se.citerus.dddsample.domain.model.cargo.TrackingId;
6
+import se.citerus.dddsample.domain.model.cargo.*;
10 7
 import se.citerus.dddsample.domain.model.location.LocationRepository;
11 8
 import static se.citerus.dddsample.domain.model.location.SampleLocations.CHICAGO;
12 9
 import static se.citerus.dddsample.domain.model.location.SampleLocations.STOCKHOLM;
@@ -21,12 +18,15 @@ public class BookingServiceTest extends TestCase {
21 18
   CargoRepository cargoRepository;
22 19
   LocationRepository locationRepository;
23 20
   RoutingService routingService;
21
+  TrackingIdGenerator trackingIdGenerator;
24 22
 
25 23
   protected void setUp() throws Exception {
26 24
     cargoRepository = createMock(CargoRepository.class);
27 25
     locationRepository = createMock(LocationRepository.class);
28 26
     routingService = createMock(RoutingService.class);
29
-    bookingService = new BookingServiceImpl(routingService, new CargoFactory(cargoRepository, locationRepository), cargoRepository, locationRepository);
27
+    trackingIdGenerator = createMock(TrackingIdGenerator.class);
28
+    CargoFactory cargoFactory = new CargoFactory(locationRepository, trackingIdGenerator);
29
+    bookingService = new BookingServiceImpl(routingService, cargoFactory, cargoRepository, locationRepository);
30 30
   }
31 31
 
32 32
   public void testRegisterNew() {
@@ -34,19 +34,19 @@ public class BookingServiceTest extends TestCase {
34 34
     UnLocode fromUnlocode = new UnLocode("USCHI");
35 35
     UnLocode toUnlocode = new UnLocode("SESTO");
36 36
 
37
-    expect(cargoRepository.nextTrackingId()).andReturn(expectedTrackingId);
37
+    expect(trackingIdGenerator.nextTrackingId()).andReturn(expectedTrackingId);
38 38
     expect(locationRepository.find(fromUnlocode)).andReturn(CHICAGO);
39 39
     expect(locationRepository.find(toUnlocode)).andReturn(STOCKHOLM);
40 40
 
41 41
     cargoRepository.store(isA(Cargo.class));
42 42
 
43
-    replay(cargoRepository, locationRepository);
43
+    replay(cargoRepository, locationRepository, trackingIdGenerator);
44 44
 
45 45
     TrackingId trackingId = bookingService.bookNewCargo(fromUnlocode, toUnlocode, new Date());
46 46
     assertEquals(expectedTrackingId, trackingId);
47 47
   }
48 48
 
49 49
   protected void tearDown() throws Exception {
50
-    verify(cargoRepository, locationRepository);
50
+    verify(cargoRepository, locationRepository, trackingIdGenerator);
51 51
   }
52 52
 }

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/application/HandlingEventServiceTest.java Dosyayı Görüntüle

@@ -48,7 +48,7 @@ public class HandlingEventServiceTest extends TestCase {
48 48
     expect(voyageRepository.find(CM001.voyageNumber())).andReturn(CM001);
49 49
     expect(locationRepository.find(STOCKHOLM.unLocode())).andReturn(STOCKHOLM);
50 50
     handlingEventRepository.store(isA(HandlingEvent.class));
51
-    applicationEvents.cargoWasHandled(isA(HandlingEvent.class));
51
+    applicationEvents.notifyOfHandlingEvent(isA(HandlingEvent.class));
52 52
 
53 53
     replay(cargoRepository, voyageRepository, handlingEventRepository, locationRepository, applicationEvents);
54 54
 

+ 2
- 2
dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoFactoryTest.java Dosyayı Görüntüle

@@ -12,14 +12,14 @@ import org.junit.Test;
12 12
 import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
13 13
 import static se.citerus.dddsample.domain.model.location.SampleLocations.HONGKONG;
14 14
 import static se.citerus.dddsample.domain.model.location.SampleLocations.ROTTERDAM;
15
-import se.citerus.dddsample.infrastructure.persistence.inmemory.CargoRepositoryInMem;
15
+import se.citerus.dddsample.infrastructure.persistence.TrackingIdGeneratorInMem;
16 16
 import se.citerus.dddsample.infrastructure.persistence.inmemory.LocationRepositoryInMem;
17 17
 
18 18
 public class CargoFactoryTest {
19 19
 
20 20
   @Test
21 21
   public void createNewCargo() {
22
-    CargoFactory cargoFactory = new CargoFactory(new CargoRepositoryInMem(), new LocationRepositoryInMem());
22
+    CargoFactory cargoFactory = new CargoFactory(new LocationRepositoryInMem(), new TrackingIdGeneratorInMem());
23 23
     Cargo cargo = cargoFactory.newCargo(
24 24
       HONGKONG.unLocode(), ROTTERDAM.unLocode(), toDate("2009-07-01")
25 25
     );

+ 57
- 114
dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/CargoTest.java Dosyayı Görüntüle

@@ -5,9 +5,10 @@ import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
5 5
 import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.*;
6 6
 import static se.citerus.dddsample.domain.model.cargo.TransportStatus.NOT_RECEIVED;
7 7
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
8
-import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8
+import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
9 9
 import se.citerus.dddsample.domain.model.location.Location;
10 10
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
11
+import se.citerus.dddsample.domain.model.shared.HandlingActivity;
11 12
 import se.citerus.dddsample.domain.model.voyage.Voyage;
12 13
 import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
13 14
 
@@ -52,10 +53,10 @@ public class CargoTest extends TestCase {
52 53
 
53 54
     Cargo cargo = new Cargo(trackingId, routeSpecification);
54 55
 
55
-    assertEquals(NOT_ROUTED, cargo.delivery().routingStatus());
56
-    assertEquals(NOT_RECEIVED, cargo.delivery().transportStatus());
57
-    assertEquals(Location.UNKNOWN, cargo.delivery().lastKnownLocation());
58
-    assertEquals(Voyage.NONE, cargo.delivery().currentVoyage());
56
+    assertEquals(NOT_ROUTED, cargo.routingStatus());
57
+    assertEquals(NOT_RECEIVED, cargo.transportStatus());
58
+    assertEquals(Location.UNKNOWN, cargo.lastKnownLocation());
59
+    assertEquals(Voyage.NONE, cargo.currentVoyage());
59 60
   }
60 61
 
61 62
   public void testRoutingStatus() throws Exception {
@@ -71,43 +72,43 @@ public class CargoTest extends TestCase {
71 72
 
72 73
     cargo.specifyNewRoute(acceptOnlyGood);
73 74
 
74
-    assertEquals(NOT_ROUTED, cargo.delivery().routingStatus());
75
+    assertEquals(NOT_ROUTED, cargo.routingStatus());
75 76
 
76 77
     cargo.assignToRoute(bad);
77
-    assertEquals(MISROUTED, cargo.delivery().routingStatus());
78
+    assertEquals(MISROUTED, cargo.routingStatus());
78 79
 
79 80
     cargo.assignToRoute(good);
80
-    assertEquals(ROUTED, cargo.delivery().routingStatus());
81
+    assertEquals(ROUTED, cargo.routingStatus());
81 82
   }
82 83
 
83 84
   public void testlastKnownLocationUnknownWhenNoEvents() throws Exception {
84 85
     Cargo cargo = new Cargo(new TrackingId("XYZ"), new RouteSpecification(STOCKHOLM, MELBOURNE, new Date()));
85 86
 
86
-    assertEquals(Location.UNKNOWN, cargo.delivery().lastKnownLocation());
87
+    assertEquals(Location.UNKNOWN, cargo.lastKnownLocation());
87 88
   }
88 89
 
89 90
   public void testlastKnownLocationReceived() throws Exception {
90 91
     Cargo cargo = populateCargoReceivedStockholm();
91 92
 
92
-    assertEquals(STOCKHOLM, cargo.delivery().lastKnownLocation());
93
+    assertEquals(STOCKHOLM, cargo.lastKnownLocation());
93 94
   }
94 95
 
95 96
   public void testlastKnownLocationClaimed() throws Exception {
96 97
     Cargo cargo = populateCargoClaimedMelbourne();
97 98
 
98
-    assertEquals(MELBOURNE, cargo.delivery().lastKnownLocation());
99
+    assertEquals(MELBOURNE, cargo.lastKnownLocation());
99 100
   }
100 101
 
101 102
   public void testlastKnownLocationUnloaded() throws Exception {
102 103
     Cargo cargo = populateCargoOffHongKong();
103 104
 
104
-    assertEquals(HONGKONG, cargo.delivery().lastKnownLocation());
105
+    assertEquals(HONGKONG, cargo.lastKnownLocation());
105 106
   }
106 107
 
107 108
   public void testlastKnownLocationloaded() throws Exception {
108 109
     Cargo cargo = populateCargoOnHamburg();
109 110
 
110
-    assertEquals(HAMBURG, cargo.delivery().lastKnownLocation());
111
+    assertEquals(HAMBURG, cargo.lastKnownLocation());
111 112
   }
112 113
 
113 114
   public void testEquality() throws Exception {
@@ -126,171 +127,113 @@ public class CargoTest extends TestCase {
126 127
 
127 128
   public void testIsUnloadedAtFinalDestination() throws Exception {
128 129
     Cargo cargo = setUpCargoWithItinerary(HANGZOU, TOKYO, NEWYORK);
129
-    assertFalse(cargo.delivery().isUnloadedAtDestination());
130
+    assertFalse(cargo.isReadyToClaim());
130 131
 
131 132
     // Adding an event unrelated to unloading at final destination
132
-
133
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
134
-    events.add(
135
-      new HandlingEvent(cargo, new Date(10), new Date(), HandlingEvent.Type.RECEIVE, HANGZOU));
136
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
137
-    assertFalse(cargo.delivery().isUnloadedAtDestination());
133
+    cargo.handled(new HandlingActivity(RECEIVE, HANGZOU));
134
+    assertFalse(cargo.isReadyToClaim());
138 135
 
139 136
     Voyage voyage = new Voyage.Builder(new VoyageNumber("0123"), HANGZOU).
140 137
       addMovement(NEWYORK, new Date(), new Date()).
141 138
       build();
142 139
 
143 140
     // Adding an unload event, but not at the final destination
144
-    events.add(
145
-      new HandlingEvent(cargo, new Date(20), new Date(), HandlingEvent.Type.UNLOAD, TOKYO, voyage));
146
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
147
-    assertFalse(cargo.delivery().isUnloadedAtDestination());
141
+    cargo.handled(new HandlingActivity(UNLOAD, TOKYO, voyage));
142
+    assertFalse(cargo.isReadyToClaim());
148 143
 
149 144
     // Adding an event in the final destination, but not unload
150
-    events.add(
151
-      new HandlingEvent(cargo, new Date(30), new Date(), HandlingEvent.Type.CUSTOMS, NEWYORK));
152
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
153
-    assertFalse(cargo.delivery().isUnloadedAtDestination());
145
+    cargo.handled(new HandlingActivity(CUSTOMS, NEWYORK));
146
+    assertFalse(cargo.isReadyToClaim());
154 147
 
155 148
     // Finally, cargo is unloaded at final destination
156
-    events.add(
157
-      new HandlingEvent(cargo, new Date(40), new Date(), HandlingEvent.Type.UNLOAD, NEWYORK, voyage));
158
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
159
-    assertTrue(cargo.delivery().isUnloadedAtDestination());
149
+    cargo.handled(new HandlingActivity(UNLOAD, NEWYORK, voyage));
150
+    assertTrue(cargo.isReadyToClaim());
160 151
   }
161 152
 
162
-  // TODO: Generate test data some better way
163 153
   private Cargo populateCargoReceivedStockholm() throws Exception {
164 154
     final Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
165
-
166
-    HandlingEvent he = new HandlingEvent(cargo, toDate("2007-12-01"), new Date(), HandlingEvent.Type.RECEIVE, STOCKHOLM);
167
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
168
-    events.add(he);
169
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
170
-
155
+    cargo.handled(new HandlingActivity(RECEIVE, STOCKHOLM));
171 156
     return cargo;
172 157
   }
173 158
 
174 159
   private Cargo populateCargoClaimedMelbourne() throws Exception {
175 160
     final Cargo cargo = populateCargoOffMelbourne();
176
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
177
-    events.add(new HandlingEvent(cargo, toDate("2007-12-09"), new Date(), HandlingEvent.Type.CLAIM, MELBOURNE));
178
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
179 161
 
162
+    cargo.handled(new HandlingActivity(CLAIM, MELBOURNE));
180 163
     return cargo;
181 164
   }
182 165
 
183 166
   private Cargo populateCargoOffHongKong() throws Exception {
184 167
     final Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
185
-
186
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
187
-    events.add(new HandlingEvent(cargo, toDate("2007-12-01"), new Date(), HandlingEvent.Type.LOAD, STOCKHOLM, crazyVoyage));
188
-    events.add(new HandlingEvent(cargo, toDate("2007-12-02"), new Date(), HandlingEvent.Type.UNLOAD, HAMBURG, crazyVoyage));
189
-
190
-    events.add(new HandlingEvent(cargo, toDate("2007-12-03"), new Date(), HandlingEvent.Type.LOAD, HAMBURG, crazyVoyage));
191
-    events.add(new HandlingEvent(cargo, toDate("2007-12-04"), new Date(), HandlingEvent.Type.UNLOAD, HONGKONG, crazyVoyage));
192
-
193
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
168
+    
169
+    cargo.handled(new HandlingActivity(LOAD, STOCKHOLM, crazyVoyage));
170
+    cargo.handled(new HandlingActivity(UNLOAD, HAMBURG, crazyVoyage));
171
+    cargo.handled(new HandlingActivity(LOAD, HAMBURG, crazyVoyage));
172
+    cargo.handled(new HandlingActivity(UNLOAD, HONGKONG, crazyVoyage));
194 173
     return cargo;
195 174
   }
196 175
 
197 176
   private Cargo populateCargoOnHamburg() throws Exception {
198 177
     final Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
199
-
200
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
201
-    events.add(new HandlingEvent(cargo, toDate("2007-12-01"), new Date(), HandlingEvent.Type.LOAD, STOCKHOLM, crazyVoyage));
202
-    events.add(new HandlingEvent(cargo, toDate("2007-12-02"), new Date(), HandlingEvent.Type.UNLOAD, HAMBURG, crazyVoyage));
203
-    events.add(new HandlingEvent(cargo, toDate("2007-12-03"), new Date(), HandlingEvent.Type.LOAD, HAMBURG, crazyVoyage));
204
-
205
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
178
+    
179
+    cargo.handled(new HandlingActivity(LOAD, STOCKHOLM, crazyVoyage));
180
+    cargo.handled(new HandlingActivity(UNLOAD, HAMBURG, crazyVoyage));
181
+    cargo.handled(new HandlingActivity(LOAD, HAMBURG, crazyVoyage));
206 182
     return cargo;
207 183
   }
208 184
 
209 185
   private Cargo populateCargoOffMelbourne() throws Exception {
210 186
     final Cargo cargo = setUpCargoWithItinerary(STOCKHOLM, HAMBURG, MELBOURNE);
211 187
 
212
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
213
-    events.add(new HandlingEvent(cargo, toDate("2007-12-01"), new Date(), HandlingEvent.Type.LOAD, STOCKHOLM, crazyVoyage));
214
-    events.add(new HandlingEvent(cargo, toDate("2007-12-02"), new Date(), HandlingEvent.Type.UNLOAD, HAMBURG, crazyVoyage));
215
-
216
-    events.add(new HandlingEvent(cargo, toDate("2007-12-03"), new Date(), HandlingEvent.Type.LOAD, HAMBURG, crazyVoyage));
217
-    events.add(new HandlingEvent(cargo, toDate("2007-12-04"), new Date(), HandlingEvent.Type.UNLOAD, HONGKONG, crazyVoyage));
188
+    cargo.handled(new HandlingActivity(LOAD, STOCKHOLM, crazyVoyage));
189
+    cargo.handled(new HandlingActivity(UNLOAD, HAMBURG, crazyVoyage));
190
+    cargo.handled(new HandlingActivity(LOAD, HAMBURG, crazyVoyage));
191
+    cargo.handled(new HandlingActivity(UNLOAD, HONGKONG, crazyVoyage));
192
+    cargo.handled(new HandlingActivity(LOAD, HONGKONG, crazyVoyage));
193
+    cargo.handled(new HandlingActivity(UNLOAD, MELBOURNE, crazyVoyage));
218 194
 
219
-    events.add(new HandlingEvent(cargo, toDate("2007-12-05"), new Date(), HandlingEvent.Type.LOAD, HONGKONG, crazyVoyage));
220
-    events.add(new HandlingEvent(cargo, toDate("2007-12-07"), new Date(), HandlingEvent.Type.UNLOAD, MELBOURNE, crazyVoyage));
221
-
222
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
223
-    return cargo;
224
-  }
225
-
226
-  private Cargo populateCargoOnHongKong() throws Exception {
227
-    final Cargo cargo = new Cargo(new TrackingId("XYZ"), new RouteSpecification(STOCKHOLM, MELBOURNE, new Date()));
228
-
229
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
230
-    events.add(new HandlingEvent(cargo, toDate("2007-12-01"), new Date(), HandlingEvent.Type.LOAD, STOCKHOLM, crazyVoyage));
231
-    events.add(new HandlingEvent(cargo, toDate("2007-12-02"), new Date(), HandlingEvent.Type.UNLOAD, HAMBURG, crazyVoyage));
232
-
233
-    events.add(new HandlingEvent(cargo, toDate("2007-12-03"), new Date(), HandlingEvent.Type.LOAD, HAMBURG, crazyVoyage));
234
-    events.add(new HandlingEvent(cargo, toDate("2007-12-04"), new Date(), HandlingEvent.Type.UNLOAD, HONGKONG, crazyVoyage));
235
-
236
-    events.add(new HandlingEvent(cargo, toDate("2007-12-05"), new Date(), HandlingEvent.Type.LOAD, HONGKONG, crazyVoyage));
237
-
238
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
239 195
     return cargo;
240 196
   }
241 197
 
242 198
   public void testIsMisdirected() throws Exception {
243 199
     //A cargo with no itinerary is not misdirected
244 200
     Cargo cargo = new Cargo(new TrackingId("TRKID"), new RouteSpecification(SHANGHAI, GOTHENBURG, new Date()));
245
-    assertFalse(cargo.delivery().isMisdirected());
201
+    assertFalse(cargo.isMisdirected());
246 202
 
247 203
     cargo = setUpCargoWithItinerary(SHANGHAI, ROTTERDAM, GOTHENBURG);
248 204
 
249 205
     //A cargo with no handling events is not misdirected
250
-    assertFalse(cargo.delivery().isMisdirected());
206
+    assertFalse(cargo.isMisdirected());
251 207
 
252 208
     //Happy path
253 209
     List<HandlingEvent> events = new ArrayList<HandlingEvent>();
254
-    events.add(new HandlingEvent(cargo, new Date(10), new Date(20), HandlingEvent.Type.RECEIVE, SHANGHAI));
255
-    events.add(new HandlingEvent(cargo, new Date(30), new Date(40), HandlingEvent.Type.LOAD, SHANGHAI, crazyVoyage));
256
-    events.add(new HandlingEvent(cargo, new Date(50), new Date(60), HandlingEvent.Type.UNLOAD, ROTTERDAM, crazyVoyage));
257
-    events.add(new HandlingEvent(cargo, new Date(70), new Date(80), HandlingEvent.Type.LOAD, ROTTERDAM, crazyVoyage));
258
-    events.add(new HandlingEvent(cargo, new Date(90), new Date(100), HandlingEvent.Type.UNLOAD, GOTHENBURG, crazyVoyage));
259
-    events.add(new HandlingEvent(cargo, new Date(110), new Date(120), HandlingEvent.Type.CLAIM, GOTHENBURG));
260
-    events.add(new HandlingEvent(cargo, new Date(130), new Date(140), HandlingEvent.Type.CUSTOMS, GOTHENBURG));
261 210
 
262
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
263
-    assertFalse(cargo.delivery().isMisdirected());
211
+    cargo.handled(new HandlingActivity(RECEIVE, SHANGHAI));
212
+    cargo.handled(new HandlingActivity(LOAD, SHANGHAI, crazyVoyage));
213
+    cargo.handled(new HandlingActivity(UNLOAD, ROTTERDAM, crazyVoyage));
214
+    cargo.handled(new HandlingActivity(LOAD, ROTTERDAM, crazyVoyage));
215
+    cargo.handled(new HandlingActivity(UNLOAD, GOTHENBURG, crazyVoyage));
216
+    cargo.handled(new HandlingActivity(CLAIM, GOTHENBURG));
217
+    cargo.handled(new HandlingActivity(CUSTOMS, GOTHENBURG));
218
+    assertFalse(cargo.isMisdirected());
264 219
 
265 220
     //Try a couple of failing ones
266 221
 
267 222
     cargo = setUpCargoWithItinerary(SHANGHAI, ROTTERDAM, GOTHENBURG);
268
-    events.add(new HandlingEvent(cargo, new Date(), new Date(), HandlingEvent.Type.RECEIVE, HANGZOU));
269
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
270 223
 
271
-    assertTrue(cargo.delivery().isMisdirected());
224
+    cargo.handled(new HandlingActivity(RECEIVE, HANGZOU));
225
+    assertTrue(cargo.isMisdirected());
272 226
 
273
-    cargo = setUpCargoWithItinerary(SHANGHAI, ROTTERDAM, GOTHENBURG);
274 227
 
275
-    events.add(new HandlingEvent(cargo, new Date(10), new Date(20), HandlingEvent.Type.RECEIVE, SHANGHAI));
276
-    events.add(new HandlingEvent(cargo, new Date(30), new Date(40), HandlingEvent.Type.LOAD, SHANGHAI, crazyVoyage));
277
-    events.add(new HandlingEvent(cargo, new Date(50), new Date(60), HandlingEvent.Type.UNLOAD, ROTTERDAM, crazyVoyage));
278
-    events.add(new HandlingEvent(cargo, new Date(70), new Date(80), HandlingEvent.Type.LOAD, ROTTERDAM, crazyVoyage));
279
-
280
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
281
-
282
-    assertTrue(cargo.delivery().isMisdirected());
283 228
 
284 229
     cargo = setUpCargoWithItinerary(SHANGHAI, ROTTERDAM, GOTHENBURG);
285 230
 
286
-    events.add(new HandlingEvent(cargo, new Date(10), new Date(20), HandlingEvent.Type.RECEIVE, SHANGHAI));
287
-    events.add(new HandlingEvent(cargo, new Date(30), new Date(40), HandlingEvent.Type.LOAD, SHANGHAI, crazyVoyage));
288
-    events.add(new HandlingEvent(cargo, new Date(50), new Date(60), HandlingEvent.Type.UNLOAD, ROTTERDAM, crazyVoyage));
289
-    events.add(new HandlingEvent(cargo, new Date(), new Date(), HandlingEvent.Type.CLAIM, ROTTERDAM));
290
-
291
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
231
+    cargo.handled(new HandlingActivity(RECEIVE, SHANGHAI));
232
+    cargo.handled(new HandlingActivity(LOAD, SHANGHAI, crazyVoyage));
233
+    cargo.handled(new HandlingActivity(UNLOAD, ROTTERDAM, crazyVoyage));
234
+    cargo.handled(new HandlingActivity(CLAIM, ROTTERDAM));
292 235
 
293
-    assertTrue(cargo.delivery().isMisdirected());
236
+    assertTrue(cargo.isMisdirected());
294 237
   }
295 238
 
296 239
   public void testCustomsClearancePoint() {

+ 16
- 17
dddsample/src/test/java/se/citerus/dddsample/domain/model/cargo/DeliveryTest.java Dosyayı Görüntüle

@@ -1,17 +1,16 @@
1 1
 package se.citerus.dddsample.domain.model.cargo;
2 2
 
3 3
 import junit.framework.TestCase;
4
-import static se.citerus.dddsample.application.util.DateTestUtil.*;
5
-import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
6
-import static se.citerus.dddsample.domain.model.location.SampleLocations.DALLAS;
7
-import static se.citerus.dddsample.domain.model.location.SampleLocations.STOCKHOLM;
4
+import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
5
+import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.MISROUTED;
6
+import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.ROUTED;
7
+import static se.citerus.dddsample.domain.model.cargo.TransportStatus.*;
8
+import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
8 9
 import se.citerus.dddsample.domain.model.location.Location;
9
-import se.citerus.dddsample.domain.model.voyage.Voyage;
10
-import static se.citerus.dddsample.domain.model.voyage.SampleVoyages.*;
10
+import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
11 11
 import se.citerus.dddsample.domain.model.shared.HandlingActivity;
12
-import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
13
-import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.*;
14
-import static se.citerus.dddsample.domain.model.cargo.TransportStatus.*;
12
+import static se.citerus.dddsample.domain.model.voyage.SampleVoyages.*;
13
+import se.citerus.dddsample.domain.model.voyage.Voyage;
15 14
 
16 15
 import java.util.Date;
17 16
 
@@ -31,7 +30,7 @@ public class DeliveryTest extends TestCase {
31 30
       Leg.deriveLeg(DALLAS_TO_HELSINKI, DALLAS, STOCKHOLM)
32 31
     );
33 32
     delivery = Delivery.initial(routeSpecification, itinerary);
34
-    projections = new Projections(delivery, itinerary, routeSpecification);
33
+    projections = new Projections(delivery, itinerary);
35 34
     Thread.sleep(1);
36 35
   }
37 36
 
@@ -53,7 +52,7 @@ public class DeliveryTest extends TestCase {
53 52
 
54 53
     HandlingActivity handlingActivity = new HandlingActivity(RECEIVE, HANGZOU);
55 54
     Delivery newDelivery = delivery.whenHandled(routeSpecification, itinerary, handlingActivity);
56
-    Projections newProjections = new Projections(newDelivery, itinerary, routeSpecification, handlingActivity);
55
+    Projections newProjections = new Projections(newDelivery, itinerary);
57 56
 
58 57
     // Changed on handling
59 58
     assertEquals(Voyage.NONE, newDelivery.currentVoyage());
@@ -76,7 +75,7 @@ public class DeliveryTest extends TestCase {
76 75
 
77 76
     handlingActivity = new HandlingActivity(LOAD, HANGZOU, HONGKONG_TO_NEW_YORK);
78 77
     newDelivery = newDelivery.whenHandled(routeSpecification, itinerary, handlingActivity);
79
-    newProjections = new Projections(newDelivery, itinerary, routeSpecification, handlingActivity);
78
+    newProjections = new Projections(newDelivery, itinerary);
80 79
 
81 80
     assertEquals(HONGKONG_TO_NEW_YORK, newDelivery.currentVoyage());
82 81
     assertEquals(HANGZOU, newDelivery.lastKnownLocation());
@@ -97,7 +96,7 @@ public class DeliveryTest extends TestCase {
97 96
 
98 97
     handlingActivity = new HandlingActivity(UNLOAD, STOCKHOLM, DALLAS_TO_HELSINKI);
99 98
     newDelivery = newDelivery.whenHandled(routeSpecification, itinerary, handlingActivity);
100
-    newProjections = new Projections(newDelivery, itinerary, routeSpecification, handlingActivity);
99
+    newProjections = new Projections(newDelivery, itinerary);
101 100
 
102 101
     assertEquals(Voyage.NONE, newDelivery.currentVoyage());
103 102
     assertEquals(STOCKHOLM, newDelivery.lastKnownLocation());
@@ -116,7 +115,7 @@ public class DeliveryTest extends TestCase {
116 115
 
117 116
     handlingActivity = new HandlingActivity(CLAIM, STOCKHOLM);
118 117
     newDelivery = newDelivery.whenHandled(routeSpecification, itinerary, handlingActivity);
119
-    newProjections = new Projections(newDelivery, itinerary, routeSpecification, handlingActivity);
118
+    newProjections = new Projections(newDelivery, itinerary);
120 119
 
121 120
     assertEquals(Voyage.NONE, newDelivery.currentVoyage());
122 121
     assertEquals(STOCKHOLM, newDelivery.lastKnownLocation());
@@ -136,7 +135,7 @@ public class DeliveryTest extends TestCase {
136 135
     // Unload in Hamburg, which is the wrong location
137 136
     HandlingActivity handlingActivity = new HandlingActivity(UNLOAD, HAMBURG, DALLAS_TO_HELSINKI);
138 137
     Delivery newDelivery = delivery.whenHandled(routeSpecification, itinerary, handlingActivity);
139
-    Projections newProjections = new Projections(newDelivery, itinerary, routeSpecification, handlingActivity);
138
+    Projections newProjections = new Projections(newDelivery, itinerary);
140 139
 
141 140
     assertEquals(Voyage.NONE, newDelivery.currentVoyage());
142 141
     assertEquals(HAMBURG, newDelivery.lastKnownLocation());
@@ -158,7 +157,7 @@ public class DeliveryTest extends TestCase {
158 157
     // New route specification, old itinerary
159 158
     RouteSpecification newRouteSpecification = routeSpecification.withOrigin(HAMBURG);
160 159
     newDelivery = newDelivery.withRoutingChange(newRouteSpecification, itinerary);
161
-    newProjections = new Projections(newDelivery, itinerary, newRouteSpecification);
160
+    newProjections = new Projections(newDelivery, itinerary);
162 161
     assertEquals(MISROUTED, newDelivery.routingStatus());
163 162
 
164 163
     // TODO is it misdirected at this point?
@@ -172,7 +171,7 @@ public class DeliveryTest extends TestCase {
172 171
     );
173 172
 
174 173
     newDelivery = newDelivery.withRoutingChange(newRouteSpecification, newItinerary);
175
-    newProjections = new Projections(newDelivery, newItinerary, newRouteSpecification);
174
+    newProjections = new Projections(newDelivery, newItinerary);
176 175
 
177 176
     assertEquals(ROUTED, newDelivery.routingStatus());
178 177
     assertFalse(newDelivery.isMisdirected());

+ 22
- 0
dddsample/src/test/java/se/citerus/dddsample/infrastructure/TrackingIdGeneratorTest.java Dosyayı Görüntüle

@@ -0,0 +1,22 @@
1
+package se.citerus.dddsample.infrastructure;
2
+
3
+import junit.framework.Assert;
4
+import junit.framework.TestCase;
5
+import se.citerus.dddsample.domain.model.cargo.TrackingId;
6
+import se.citerus.dddsample.domain.model.cargo.TrackingIdGenerator;
7
+import se.citerus.dddsample.infrastructure.persistence.TrackingIdGeneratorInMem;
8
+
9
+public class TrackingIdGeneratorTest extends TestCase {
10
+
11
+  TrackingIdGenerator trackingIdGenerator = new TrackingIdGeneratorInMem();
12
+
13
+  public void testNextTrackingId() {
14
+    TrackingId trackingId = trackingIdGenerator.nextTrackingId();
15
+    Assert.assertNotNull(trackingId);
16
+
17
+    TrackingId trackingId2 = trackingIdGenerator.nextTrackingId();
18
+    Assert.assertNotNull(trackingId2);
19
+    Assert.assertFalse(trackingId.equals(trackingId2));
20
+  }
21
+
22
+}

+ 0
- 11
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java Dosyayı Görüntüle

@@ -46,8 +46,6 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
46 46
     assertEquals(HONGKONG, cargo.routeSpecification().origin());
47 47
     assertEquals(HELSINKI, cargo.routeSpecification().destination());
48 48
 
49
-    assertNotNull(cargo.delivery());
50
-
51 49
     final List<HandlingEvent> events = handlingEventRepository.lookupHandlingHistoryOfCargo(cargo).distinctEventsByCompletionTime();
52 50
     assertEquals(2, events.size());
53 51
 
@@ -161,15 +159,6 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
161 159
     assertEquals(6, all.size());
162 160
   }
163 161
 
164
-  public void testNextTrackingId() {
165
-    TrackingId trackingId = cargoRepository.nextTrackingId();
166
-    assertNotNull(trackingId);
167
-
168
-    TrackingId trackingId2 = cargoRepository.nextTrackingId();
169
-    assertNotNull(trackingId2);
170
-    assertFalse(trackingId.equals(trackingId2));
171
-  }
172
-
173 162
   public void testFindCargosOnVoyage() {
174 163
     Voyage voyage = voyageRepository.find(new VoyageNumber("0101"));
175 164
     List<Cargo> cargos = cargoRepository.findCargosOnVoyage(voyage);

+ 1
- 1
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryTest.java Dosyayı Görüntüle

@@ -65,7 +65,7 @@ public class HandlingEventRepositoryTest extends AbstractRepositoryTest {
65 65
     HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
66 66
     assertEquals(cargo, handlingEvent.cargo());
67 67
     assertEquals(toDate("2007-09-27", "05:00"), handlingEvent.completionTime());
68
-    assertEquals(new HandlingActivity(HandlingEvent.Type.CLAIM, MELBOURNE), handlingEvent.handlingActivity());
68
+    assertEquals(new HandlingActivity(HandlingEvent.Type.CLAIM, MELBOURNE), handlingEvent.activity());
69 69
   }
70 70
 
71 71
 }

dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CarrierMovementRepositoryTest.java → dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/VoyageRepositoryTest.java Dosyayı Görüntüle

@@ -4,7 +4,7 @@ import se.citerus.dddsample.domain.model.voyage.Voyage;
4 4
 import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
5 5
 import se.citerus.dddsample.domain.model.voyage.VoyageRepository;
6 6
 
7
-public class CarrierMovementRepositoryTest extends AbstractRepositoryTest {
7
+public class VoyageRepositoryTest extends AbstractRepositoryTest {
8 8
 
9 9
   VoyageRepository voyageRepository;
10 10
 

+ 1
- 11
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/CargoRepositoryInMem.java Dosyayı Görüntüle

@@ -4,7 +4,6 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
4 4
 import se.citerus.dddsample.domain.model.cargo.CargoRepository;
5 5
 import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
6 6
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
7
-import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8 7
 import se.citerus.dddsample.domain.model.location.Location;
9 8
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
10 9
 import se.citerus.dddsample.domain.model.voyage.Voyage;
@@ -38,7 +37,7 @@ public class CargoRepositoryInMem implements CargoRepository {
38 37
   public List<Cargo> findCargosOnVoyage(Voyage voyage) {
39 38
     List<Cargo> onVoyage = new ArrayList<Cargo>();
40 39
     for (Cargo cargo : cargoDb.values()) {
41
-      if (voyage.sameIdentityAs(cargo.delivery().currentVoyage())) {
40
+      if (voyage.sameIdentityAs(cargo.currentVoyage())) {
42 41
         onVoyage.add(cargo);
43 42
       }
44 43
     }
@@ -51,14 +50,6 @@ public class CargoRepositoryInMem implements CargoRepository {
51 50
   }
52 51
 
53 52
   @Override
54
-  public TrackingId nextTrackingId() {
55
-    String random = UUID.randomUUID().toString().toUpperCase();
56
-    return new TrackingId(
57
-      random.substring(0, random.indexOf("-"))
58
-    );
59
-  }
60
-
61
-  @Override
62 53
   public List<Cargo> findAll() {
63 54
     return new ArrayList<Cargo>(cargoDb.values());
64 55
   }
@@ -87,7 +78,6 @@ public class CargoRepositoryInMem implements CargoRepository {
87 78
 
88 79
     final RouteSpecification routeSpecification = new RouteSpecification(origin, destination, new Date());
89 80
     final Cargo cargo = new Cargo(trackingId, routeSpecification);
90
-    cargo.deriveDeliveryProgress(HandlingHistory.emptyForCargo(cargo));
91 81
 
92 82
     return cargo;
93 83
   }

+ 14
- 0
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/HandlingEventRepositoryInMem.java Dosyayı Görüntüle

@@ -5,6 +5,7 @@ import se.citerus.dddsample.domain.model.cargo.TrackingId;
5 5
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
6 6
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
7 7
 import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8
+import se.citerus.dddsample.domain.model.shared.EventSequenceNumber;
8 9
 
9 10
 import java.util.*;
10 11
 
@@ -20,6 +21,19 @@ public class HandlingEventRepositoryInMem implements HandlingEventRepository {
20 21
   };
21 22
 
22 23
   @Override
24
+  public HandlingEvent find(EventSequenceNumber eventSequenceNumber) {
25
+    for (List<HandlingEvent> handlingEvents : eventMap.values()) {
26
+      for (HandlingEvent handlingEvent : handlingEvents) {
27
+        if (handlingEvent.sequenceNumber().sameValueAs(eventSequenceNumber)) {
28
+          return handlingEvent;
29
+        }
30
+      }
31
+    }
32
+
33
+    return null;
34
+  }
35
+
36
+  @Override
23 37
   public void store(HandlingEvent event) {
24 38
     final TrackingId trackingId = event.cargo().trackingId();
25 39
     List<HandlingEvent> list = eventMap.get(trackingId);

+ 5
- 8
dddsample/src/test/java/se/citerus/dddsample/interfaces/tracking/CargoTrackingViewAdapterTest.java Dosyayı Görüntüle

@@ -6,9 +6,9 @@ import se.citerus.dddsample.domain.model.cargo.Cargo;
6 6
 import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
7 7
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
8 8
 import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9
-import se.citerus.dddsample.domain.model.handling.HandlingHistory;
10 9
 import static se.citerus.dddsample.domain.model.location.SampleLocations.HANGZOU;
11 10
 import static se.citerus.dddsample.domain.model.location.SampleLocations.HELSINKI;
11
+import se.citerus.dddsample.domain.model.shared.HandlingActivity;
12 12
 import static se.citerus.dddsample.domain.model.voyage.SampleVoyages.CM001;
13 13
 
14 14
 import java.util.*;
@@ -27,18 +27,15 @@ public class CargoTrackingViewAdapterTest extends TestCase {
27 27
 //  events as "expected", whereas an empty itinerary should actually consider
28 28
 //  any event unexpected. (ie empty itinerary means nothing happens.)
29 29
 
30
-    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
31
-    events.add(new HandlingEvent(cargo, new Date(1), new Date(2), HandlingEvent.Type.RECEIVE, HANGZOU));
32
-
33
-    events.add(new HandlingEvent(cargo, new Date(3), new Date(4), HandlingEvent.Type.LOAD, HANGZOU, CM001));
34
-    events.add(new HandlingEvent(cargo, new Date(5), new Date(6), HandlingEvent.Type.UNLOAD, HELSINKI, CM001));
35
-
36
-    cargo.deriveDeliveryProgress(HandlingHistory.fromEvents(events));
30
+    cargo.handled(new HandlingActivity(HandlingEvent.Type.RECEIVE, HANGZOU));
31
+    cargo.handled(new HandlingActivity(HandlingEvent.Type.LOAD, HANGZOU, CM001));
32
+    cargo.handled(new HandlingActivity(HandlingEvent.Type.UNLOAD, HELSINKI, CM001));
37 33
 
38 34
     StaticApplicationContext applicationContext = new StaticApplicationContext();
39 35
     applicationContext.addMessage("cargo.status.IN_PORT", Locale.GERMAN, "In port {0}");
40 36
     applicationContext.refresh();
41 37
 
38
+    List<HandlingEvent> events = new ArrayList<HandlingEvent>();
42 39
     CargoTrackingViewAdapter adapter = new CargoTrackingViewAdapter(cargo, applicationContext, Locale.GERMAN, events);
43 40
 
44 41
     assertEquals("XYZ", adapter.getTrackingId());

+ 75
- 74
dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java Dosyayı Görüntüle

@@ -23,6 +23,7 @@ import static se.citerus.dddsample.domain.model.voyage.Voyage.NONE;
23 23
 import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
24 24
 import se.citerus.dddsample.domain.model.voyage.VoyageRepository;
25 25
 import se.citerus.dddsample.domain.service.RoutingService;
26
+import se.citerus.dddsample.infrastructure.persistence.TrackingIdGeneratorInMem;
26 27
 import se.citerus.dddsample.infrastructure.persistence.inmemory.CargoRepositoryInMem;
27 28
 import se.citerus.dddsample.infrastructure.persistence.inmemory.HandlingEventRepositoryInMem;
28 29
 import se.citerus.dddsample.infrastructure.persistence.inmemory.LocationRepositoryInMem;
@@ -44,11 +45,11 @@ public class CargoLifecycleScenarioTest {
44 45
   VoyageRepository voyageRepository = new VoyageRepositoryInMem();
45 46
 
46 47
   HandlingEventFactory handlingEventFactory = new HandlingEventFactory(cargoRepository, voyageRepository, locationRepository);
47
-  CargoFactory cargoFactory = new CargoFactory(cargoRepository, locationRepository);
48
+  CargoFactory cargoFactory = new CargoFactory(locationRepository, new TrackingIdGeneratorInMem());
48 49
 
49 50
   /**
50 51
    * This is a domain service interface, whose implementation
51
-   * is part of the infrastructure layer (remote call to external system).
52
+   * is part of the infrastructure layer (re mote call to external system).
52 53
    * <p/>
53 54
    * It is stubbed in this test.
54 55
    */
@@ -98,11 +99,11 @@ public class CargoLifecycleScenarioTest {
98 99
   private void checkDeliveryAfterUnloadInLongBeach() {
99 100
     Cargo cargo = cargoRepository.find(trackingId);
100 101
 
101
-    assertThat(cargo.delivery().currentVoyage(), is(NONE));
102
-    assertThat(cargo.delivery().lastKnownLocation(), is(LONGBEACH));
103
-    assertThat(cargo.delivery().transportStatus(), is(IN_PORT));
104
-    assertFalse(cargo.delivery().isMisdirected());
105
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(LOAD, LONGBEACH, v250)));
102
+    assertThat(cargo.currentVoyage(), is(NONE));
103
+    assertThat(cargo.lastKnownLocation(), is(LONGBEACH));
104
+    assertThat(cargo.transportStatus(), is(IN_PORT));
105
+    assertFalse(cargo.isMisdirected());
106
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(LOAD, LONGBEACH, v250)));
106 107
   }
107 108
 
108 109
   private void loadInLongBeach() throws CannotCreateHandlingEventException {
@@ -112,11 +113,11 @@ public class CargoLifecycleScenarioTest {
112 113
   private void checkDeliveryAfterLoadInLongBeach() {
113 114
     Cargo cargo = cargoRepository.find(trackingId);
114 115
 
115
-    assertThat(cargo.delivery().currentVoyage(), is(v250));
116
-    assertThat(cargo.delivery().lastKnownLocation(), is(LONGBEACH));
117
-    assertThat(cargo.delivery().transportStatus(), is(ONBOARD_CARRIER));
118
-    assertFalse(cargo.delivery().isMisdirected());
119
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(UNLOAD, NEWYORK, v250)));
116
+    assertThat(cargo.currentVoyage(), is(v250));
117
+    assertThat(cargo.lastKnownLocation(), is(LONGBEACH));
118
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
119
+    assertFalse(cargo.isMisdirected());
120
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(UNLOAD, NEWYORK, v250)));
120 121
   }
121 122
 
122 123
   private void unloadInNewYork() throws CannotCreateHandlingEventException {
@@ -126,11 +127,11 @@ public class CargoLifecycleScenarioTest {
126 127
   private void checkDeliveryAfterUnloadInNewYork() {
127 128
     Cargo cargo = cargoRepository.find(trackingId);
128 129
 
129
-    assertThat(cargo.delivery().currentVoyage(), is(NONE));
130
-    assertThat(cargo.delivery().lastKnownLocation(), is(NEWYORK));
131
-    assertThat(cargo.delivery().transportStatus(), is(IN_PORT));
132
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(LOAD, NEWYORK, v200)));
133
-    assertFalse(cargo.delivery().isMisdirected());
130
+    assertThat(cargo.currentVoyage(), is(NONE));
131
+    assertThat(cargo.lastKnownLocation(), is(NEWYORK));
132
+    assertThat(cargo.transportStatus(), is(IN_PORT));
133
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(LOAD, NEWYORK, v200)));
134
+    assertFalse(cargo.isMisdirected());
134 135
   }
135 136
 
136 137
   private void loadInNewYork() throws CannotCreateHandlingEventException {
@@ -140,11 +141,11 @@ public class CargoLifecycleScenarioTest {
140 141
   private void checkDeliveryAfterLoadInNewYork() {
141 142
     Cargo cargo = cargoRepository.find(trackingId);
142 143
 
143
-    assertThat(cargo.delivery().currentVoyage(), is(v200));
144
-    assertThat(cargo.delivery().lastKnownLocation(), is(NEWYORK));
145
-    assertThat(cargo.delivery().transportStatus(), is(ONBOARD_CARRIER));
146
-    assertFalse(cargo.delivery().isMisdirected());
147
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(UNLOAD, STOCKHOLM, v200)));
144
+    assertThat(cargo.currentVoyage(), is(v200));
145
+    assertThat(cargo.lastKnownLocation(), is(NEWYORK));
146
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
147
+    assertFalse(cargo.isMisdirected());
148
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(UNLOAD, STOCKHOLM, v200)));
148 149
   }
149 150
 
150 151
   @Test
@@ -211,20 +212,20 @@ public class CargoLifecycleScenarioTest {
211 212
   public void checkDeliveryAfterBooking() throws Exception {
212 213
     Cargo cargo = cargoRepository.find(trackingId);
213 214
 
214
-    assertThat(cargo.delivery().transportStatus(), is(NOT_RECEIVED));
215
-    assertThat(cargo.delivery().routingStatus(), is(NOT_ROUTED));
216
-    assertFalse(cargo.delivery().isMisdirected());
217
-    assertNull(cargo.projections().estimatedTimeOfArrival());
218
-    assertNull(cargo.projections().nextExpectedActivity());
215
+    assertThat(cargo.transportStatus(), is(NOT_RECEIVED));
216
+    assertThat(cargo.routingStatus(), is(NOT_ROUTED));
217
+    assertFalse(cargo.isMisdirected());
218
+    assertNull(cargo.estimatedTimeOfArrival());
219
+    assertNull(cargo.nextExpectedActivity());
219 220
   }
220 221
 
221 222
   public void checkDeliveryAfterRouting() throws Exception {
222 223
     Cargo cargo = cargoRepository.find(trackingId);
223 224
 
224
-    assertThat(cargo.delivery().transportStatus(), is(NOT_RECEIVED));
225
-    assertThat(cargo.delivery().routingStatus(), is(ROUTED));
226
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(RECEIVE, HONGKONG)));
227
-    assertNotNull(cargo.projections().estimatedTimeOfArrival());
225
+    assertThat(cargo.transportStatus(), is(NOT_RECEIVED));
226
+    assertThat(cargo.routingStatus(), is(ROUTED));
227
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(RECEIVE, HONGKONG)));
228
+    assertNotNull(cargo.estimatedTimeOfArrival());
228 229
   }
229 230
 
230 231
   public void receiveInHongkong() throws CannotCreateHandlingEventException {
@@ -233,8 +234,8 @@ public class CargoLifecycleScenarioTest {
233 234
 
234 235
   private void checkDeliveryAfterReceiveInHongkong() {
235 236
     Cargo cargo = cargoRepository.find(trackingId);
236
-    assertThat(cargo.delivery().transportStatus(), is(IN_PORT));
237
-    assertThat(cargo.delivery().lastKnownLocation(), is(HONGKONG));
237
+    assertThat(cargo.transportStatus(), is(IN_PORT));
238
+    assertThat(cargo.lastKnownLocation(), is(HONGKONG));
238 239
   }
239 240
 
240 241
   public void loadInHongkong() throws CannotCreateHandlingEventException {
@@ -245,11 +246,11 @@ public class CargoLifecycleScenarioTest {
245 246
     // Check current state - should be ok
246 247
     Cargo cargo = cargoRepository.find(trackingId);
247 248
 
248
-    assertThat(cargo.delivery().currentVoyage(), is(v100));
249
-    assertThat(cargo.delivery().lastKnownLocation(), is(HONGKONG));
250
-    assertThat(cargo.delivery().transportStatus(), is(ONBOARD_CARRIER));
251
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(UNLOAD, LONGBEACH, v100)));
252
-    assertFalse(cargo.delivery().isMisdirected());
249
+    assertThat(cargo.currentVoyage(), is(v100));
250
+    assertThat(cargo.lastKnownLocation(), is(HONGKONG));
251
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
252
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(UNLOAD, LONGBEACH, v100)));
253
+    assertFalse(cargo.isMisdirected());
253 254
   }
254 255
 
255 256
   public void unloadInTokyo() throws CannotCreateHandlingEventException {
@@ -260,11 +261,11 @@ public class CargoLifecycleScenarioTest {
260 261
   private void checkDeliveryAfterUnloadInTokyo() {
261 262
     Cargo cargo = cargoRepository.find(trackingId);
262 263
     // Check current state - cargo is misdirected!
263
-    assertThat(cargo.delivery().currentVoyage(), is(NONE));
264
-    assertThat(cargo.delivery().lastKnownLocation(), is(TOKYO));
265
-    assertThat(cargo.delivery().transportStatus(), is(IN_PORT));
266
-    assertTrue(cargo.delivery().isMisdirected());
267
-    assertNull(cargo.projections().nextExpectedActivity());
264
+    assertThat(cargo.currentVoyage(), is(NONE));
265
+    assertThat(cargo.lastKnownLocation(), is(TOKYO));
266
+    assertThat(cargo.transportStatus(), is(IN_PORT));
267
+    assertTrue(cargo.isMisdirected());
268
+    assertNull(cargo.nextExpectedActivity());
268 269
   }
269 270
 
270 271
   public void specifyNewRouteFromTokyoToStockholm() {
@@ -281,8 +282,8 @@ public class CargoLifecycleScenarioTest {
281 282
     Cargo cargo = cargoRepository.find(trackingId);
282 283
 
283 284
     // The old itinerary does not satisfy the new specification
284
-    assertThat(cargo.delivery().routingStatus(), is(MISROUTED));
285
-    assertNull(cargo.projections().nextExpectedActivity());
285
+    assertThat(cargo.routingStatus(), is(MISROUTED));
286
+    assertNull(cargo.nextExpectedActivity());
286 287
   }
287 288
 
288 289
   public void assignToNewRouteFromTokyoToStockholm() {
@@ -300,9 +301,9 @@ public class CargoLifecycleScenarioTest {
300 301
     Cargo cargo = cargoRepository.find(trackingId);
301 302
 
302 303
     // New itinerary should satisfy new route
303
-    assertThat(cargo.delivery().routingStatus(), is(ROUTED));
304
-    assertFalse(cargo.delivery().isMisdirected());
305
-    assertEquals(new HandlingActivity(LOAD, TOKYO, v300), cargo.projections().nextExpectedActivity());
304
+    assertThat(cargo.routingStatus(), is(ROUTED));
305
+    assertFalse(cargo.isMisdirected());
306
+    assertEquals(new HandlingActivity(LOAD, TOKYO, v300), cargo.nextExpectedActivity());
306 307
   }
307 308
 
308 309
   public void loadInTokyo() throws CannotCreateHandlingEventException {
@@ -312,11 +313,11 @@ public class CargoLifecycleScenarioTest {
312 313
   private void checkDeliveryAfterLoadInTokyo() {
313 314
     Cargo cargo = cargoRepository.find(trackingId);
314 315
     // Check current state - should be ok
315
-    assertThat(cargo.delivery().currentVoyage(), is(v300));
316
-    assertThat(cargo.delivery().lastKnownLocation(), is(TOKYO));
317
-    assertThat(cargo.delivery().transportStatus(), is(ONBOARD_CARRIER));
318
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(UNLOAD, HAMBURG, v300)));
319
-    assertFalse(cargo.delivery().isMisdirected());
316
+    assertThat(cargo.currentVoyage(), is(v300));
317
+    assertThat(cargo.lastKnownLocation(), is(TOKYO));
318
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
319
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(UNLOAD, HAMBURG, v300)));
320
+    assertFalse(cargo.isMisdirected());
320 321
   }
321 322
 
322 323
   public void unloadInHamburg() throws CannotCreateHandlingEventException {
@@ -327,11 +328,11 @@ public class CargoLifecycleScenarioTest {
327 328
     Cargo cargo = cargoRepository.find(trackingId);
328 329
     // Check current state - should be ok
329 330
 
330
-    assertThat(cargo.delivery().currentVoyage(), is(NONE));
331
-    assertThat(cargo.delivery().lastKnownLocation(), is(HAMBURG));
332
-    assertThat(cargo.delivery().transportStatus(), is(IN_PORT));
333
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(LOAD, HAMBURG, v400)));
334
-    assertFalse(cargo.delivery().isMisdirected());
331
+    assertThat(cargo.currentVoyage(), is(NONE));
332
+    assertThat(cargo.lastKnownLocation(), is(HAMBURG));
333
+    assertThat(cargo.transportStatus(), is(IN_PORT));
334
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(LOAD, HAMBURG, v400)));
335
+    assertFalse(cargo.isMisdirected());
335 336
   }
336 337
 
337 338
   public void loadInHamburg() throws CannotCreateHandlingEventException {
@@ -342,11 +343,11 @@ public class CargoLifecycleScenarioTest {
342 343
     Cargo cargo = cargoRepository.find(trackingId);
343 344
     // Check current state - should be ok
344 345
 
345
-    assertThat(cargo.delivery().currentVoyage(), is(v400));
346
-    assertThat(cargo.delivery().lastKnownLocation(), is(HAMBURG));
347
-    assertThat(cargo.delivery().transportStatus(), is(ONBOARD_CARRIER));
348
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(UNLOAD, STOCKHOLM, v400)));
349
-    assertFalse(cargo.delivery().isMisdirected());
346
+    assertThat(cargo.currentVoyage(), is(v400));
347
+    assertThat(cargo.lastKnownLocation(), is(HAMBURG));
348
+    assertThat(cargo.transportStatus(), is(ONBOARD_CARRIER));
349
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(UNLOAD, STOCKHOLM, v400)));
350
+    assertFalse(cargo.isMisdirected());
350 351
   }
351 352
 
352 353
   public void unloadInStockholmOffOf(Voyage voyage) throws CannotCreateHandlingEventException {
@@ -356,11 +357,11 @@ public class CargoLifecycleScenarioTest {
356 357
   private void checkDeliveryAfterUnloadInStockholm() {
357 358
     Cargo cargo = cargoRepository.find(trackingId);
358 359
     // Check current state - should be ok
359
-    assertThat(cargo.delivery().currentVoyage(), is(NONE));
360
-    assertThat(cargo.delivery().lastKnownLocation(), is(STOCKHOLM));
361
-    assertThat(cargo.delivery().transportStatus(), is(IN_PORT));
362
-    assertThat(cargo.projections().nextExpectedActivity(), is(new HandlingActivity(CLAIM, STOCKHOLM)));
363
-    assertFalse(cargo.delivery().isMisdirected());
360
+    assertThat(cargo.currentVoyage(), is(NONE));
361
+    assertThat(cargo.lastKnownLocation(), is(STOCKHOLM));
362
+    assertThat(cargo.transportStatus(), is(IN_PORT));
363
+    assertThat(cargo.nextExpectedActivity(), is(new HandlingActivity(CLAIM, STOCKHOLM)));
364
+    assertFalse(cargo.isMisdirected());
364 365
   }
365 366
 
366 367
   private void claimInStockholm() throws CannotCreateHandlingEventException {
@@ -370,11 +371,11 @@ public class CargoLifecycleScenarioTest {
370 371
   private void checkDeliveryAfterClaimInStockholm() {
371 372
     Cargo cargo = cargoRepository.find(trackingId);
372 373
     // Check current state - should be ok
373
-    assertThat(cargo.delivery().currentVoyage(), is(NONE));
374
-    assertThat(cargo.delivery().lastKnownLocation(), is(STOCKHOLM));
375
-    assertThat(cargo.delivery().transportStatus(), is(CLAIMED));
376
-    assertFalse(cargo.delivery().isMisdirected());
377
-    assertNull(cargo.projections().nextExpectedActivity());
374
+    assertThat(cargo.currentVoyage(), is(NONE));
375
+    assertThat(cargo.lastKnownLocation(), is(STOCKHOLM));
376
+    assertThat(cargo.transportStatus(), is(CLAIMED));
377
+    assertFalse(cargo.isMisdirected());
378
+    assertNull(cargo.nextExpectedActivity());
378 379
   }
379 380
 
380 381
   private void createHandlingEventAndUpdateAggregates(Date completionTime, Voyage voyage, Location location, HandlingEvent.Type type) throws CannotCreateHandlingEventException {
@@ -391,7 +392,7 @@ public class CargoLifecycleScenarioTest {
391 392
   private void updateCargoAggregate() {
392 393
     Cargo cargo = cargoRepository.find(trackingId);
393 394
     HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
394
-    cargo.handled(handlingEvent.handlingActivity());
395
+    cargo.handled(handlingEvent.activity());
395 396
     cargoRepository.store(cargo);
396 397
   }
397 398
 

+ 12
- 6
dddsample/src/test/java/se/citerus/dddsample/scenario/VoyageRescheduledScenarioTest.java Dosyayı Görüntüle

@@ -21,7 +21,7 @@ import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
21 21
 import static se.citerus.dddsample.domain.model.voyage.SampleVoyages.*;
22 22
 import se.citerus.dddsample.domain.model.voyage.Voyage;
23 23
 import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
24
-import se.citerus.dddsample.infrastructure.persistence.inmemory.CargoRepositoryInMem;
24
+import se.citerus.dddsample.infrastructure.persistence.TrackingIdGeneratorInMem;
25 25
 import se.citerus.dddsample.infrastructure.persistence.inmemory.LocationRepositoryInMem;
26 26
 
27 27
 import java.util.Date;
@@ -39,7 +39,7 @@ public class VoyageRescheduledScenarioTest {
39 39
     voyage1 = new Voyage(new VoyageNumber("V1"), HONGKONG_TO_NEW_YORK.schedule());
40 40
     voyage2 = new Voyage(new VoyageNumber("V2"), NEW_YORK_TO_DALLAS.schedule());
41 41
     voyage3 = new Voyage(new VoyageNumber("V3"), DALLAS_TO_HELSINKI.schedule());
42
-    CargoFactory cargoFactory = new CargoFactory(new CargoRepositoryInMem(), new LocationRepositoryInMem());
42
+    CargoFactory cargoFactory = new CargoFactory(new LocationRepositoryInMem(), new TrackingIdGeneratorInMem());
43 43
     cargo = cargoFactory.newCargo(HANGZOU.unLocode(), STOCKHOLM.unLocode(), toDate("2008-12-23"));
44 44
     Itinerary itinerary = new Itinerary(
45 45
       Leg.deriveLeg(voyage1, HANGZOU, NEWYORK),
@@ -51,7 +51,7 @@ public class VoyageRescheduledScenarioTest {
51 51
 
52 52
   @Test
53 53
   public void voyageIsRescheduledWithMaintainableRoute() {
54
-    assertThat(cargo.delivery().routingStatus(), is(ROUTED));
54
+    assertThat(cargo.routingStatus(), is(ROUTED));
55 55
 
56 56
     Date oldDepartureTime = toDate("2008-10-24", "07:00");
57 57
 
@@ -73,12 +73,12 @@ public class VoyageRescheduledScenarioTest {
73 73
 
74 74
     // Now the cargo aggregate is updated to reflect the scheduling change!
75 75
     assertThat(cargo.itinerary().loadTimeAt(NEWYORK), is(newDepartureTime));
76
-    assertThat(cargo.delivery().routingStatus(), is(ROUTED));
76
+    assertThat(cargo.routingStatus(), is(ROUTED));
77 77
   }
78 78
 
79 79
   @Test
80 80
   public void voyageIsRescheduledWithUnmaintainableRoute() {
81
-    assertThat(cargo.delivery().routingStatus(), is(ROUTED));
81
+    assertThat(cargo.routingStatus(), is(ROUTED));
82 82
 
83 83
     // Voyage1 arrives in NYC at 2008-10-23 23:10
84 84
     // Now rescheduling the departure of voyage2 to BEFORE
@@ -86,6 +86,8 @@ public class VoyageRescheduledScenarioTest {
86 86
     // keep the latter part of the old itinerary, and the new itinerary
87 87
     // is therefore truncated after unload in NYC.
88 88
 
89
+    // TODO delay the arrival instead of advancing the departure
90
+
89 91
     Date newDepartureTime = toDate("2008-10-23", "18:30");
90 92
     voyage2.departureRescheduled(NEWYORK, newDepartureTime);
91 93
 
@@ -93,9 +95,13 @@ public class VoyageRescheduledScenarioTest {
93 95
     Itinerary truncatedItinerary = cargo.itinerary().withRescheduledVoyage(voyage2);
94 96
     assertThat(truncatedItinerary.lastLeg().unloadLocation(), is(NEWYORK));
95 97
 
98
+    //Or... The Itinerary is created with an 'Illegal Connection' based on a coomparison of
99
+    //each transfer with a Location.minimumAllowedConnectionTime(). Since Loation is an entity
100
+    //we don't allow Itinerary to dynamically use the property directly because it is not immutable.
101
+    
96 102
     // The cargo enters MISROUTED state
97 103
     cargo.assignToRoute(truncatedItinerary);
98
-    assertThat(cargo.delivery().routingStatus(), is(MISROUTED));
104
+    assertThat(cargo.routingStatus(), is(MISROUTED));
99 105
   }
100 106
 
101 107
 }