Bläddra i källkod

Improved cargo delivery updater, added itinerary updater.

Migrated parts of the code base to use the cargo.handled(HandlingActivity) way of pushing aggregate updates.
peter_backlund 17 år sedan
förälder
incheckning
e7fb6b9853
14 ändrade filer med 171 tillägg och 24 borttagningar
  1. 5
    3
      dddsample/src/main/java/se/citerus/dddsample/application/event/CargoDeliveryUpdater.java
  2. 43
    0
      dddsample/src/main/java/se/citerus/dddsample/application/event/ItineraryUpdater.java
  3. 1
    2
      dddsample/src/main/java/se/citerus/dddsample/application/event/ReadyToClaimNotfier.java
  4. 1
    1
      dddsample/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java
  5. 8
    7
      dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java
  6. 9
    1
      dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/CargoRepository.java
  7. 7
    0
      dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEventRepository.java
  8. 13
    1
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.java
  9. 9
    0
      dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryHibernate.java
  10. 18
    0
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java
  11. 11
    0
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryTest.java
  12. 16
    1
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/CargoRepositoryInMem.java
  13. 19
    4
      dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/HandlingEventRepositoryInMem.java
  14. 11
    4
      dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java

+ 5
- 3
dddsample/src/main/java/se/citerus/dddsample/application/event/CargoDeliveryUpdater.java Visa fil

@@ -6,8 +6,8 @@ import org.springframework.transaction.annotation.Transactional;
6 6
 import se.citerus.dddsample.domain.model.cargo.Cargo;
7 7
 import se.citerus.dddsample.domain.model.cargo.CargoRepository;
8 8
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
9
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
9 10
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
10
-import se.citerus.dddsample.domain.model.handling.HandlingHistory;
11 11
 
12 12
 public class CargoDeliveryUpdater {
13 13
 
@@ -27,8 +27,10 @@ public class CargoDeliveryUpdater {
27 27
   @Transactional
28 28
   public void updateDelivery(final TrackingId trackingId) {
29 29
     final Cargo cargo = cargoRepository.find(trackingId);
30
-    final HandlingHistory handlingHistory = handlingEventRepository.lookupHandlingHistoryOfCargo(cargo);
31
-    cargo.deriveDeliveryProgress(handlingHistory);
30
+    final HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
31
+
32
+    // TODO still doesn't sound right...cargo.updateDelivery()?
33
+    cargo.handled(handlingEvent.handlingActivity());
32 34
 
33 35
     cargoRepository.store(cargo);
34 36
     applicationEvents.cargoDeliveryWasUpdated(cargo);

+ 43
- 0
dddsample/src/main/java/se/citerus/dddsample/application/event/ItineraryUpdater.java Visa fil

@@ -0,0 +1,43 @@
1
+/**
2
+ * Purpose
3
+ * @author peter
4
+ * @created 2009-aug-04
5
+ * $Id$
6
+ */
7
+package se.citerus.dddsample.application.event;
8
+
9
+import org.apache.commons.logging.Log;
10
+import org.apache.commons.logging.LogFactory;
11
+import org.springframework.transaction.annotation.Transactional;
12
+import se.citerus.dddsample.domain.model.cargo.Cargo;
13
+import se.citerus.dddsample.domain.model.cargo.CargoRepository;
14
+import se.citerus.dddsample.domain.model.cargo.Itinerary;
15
+import se.citerus.dddsample.domain.model.voyage.Voyage;
16
+import se.citerus.dddsample.domain.model.voyage.VoyageNumber;
17
+import se.citerus.dddsample.domain.model.voyage.VoyageRepository;
18
+
19
+import java.util.List;
20
+
21
+public final class ItineraryUpdater {
22
+
23
+  private VoyageRepository voyageRepository;
24
+  private CargoRepository cargoRepository;
25
+  private static final Log LOG = LogFactory.getLog(ItineraryUpdater.class);
26
+
27
+  public ItineraryUpdater(final VoyageRepository voyageRepository, final CargoRepository cargoRepository) {
28
+    this.voyageRepository = voyageRepository;
29
+    this.cargoRepository = cargoRepository;
30
+  }
31
+
32
+  @Transactional
33
+  public void updateItineraries(final VoyageNumber voyageNumber) {
34
+    final Voyage voyage = voyageRepository.find(voyageNumber);
35
+    final List<Cargo> affectedCargos = cargoRepository.findCargosOnVoyage(voyage);
36
+    for (final Cargo cargo : affectedCargos) {
37
+      final Itinerary newItinerary = cargo.itinerary().withRescheduledVoyage(voyage);
38
+      cargo.assignToRoute(newItinerary);
39
+      LOG.info("Updated itinerary of cargo " + cargo);
40
+    }
41
+  }
42
+
43
+}

+ 1
- 2
dddsample/src/main/java/se/citerus/dddsample/application/event/ReadyToClaimNotfier.java Visa fil

@@ -16,7 +16,6 @@ import se.citerus.dddsample.domain.model.cargo.TrackingId;
16 16
 public class ReadyToClaimNotfier {
17 17
 
18 18
   private CargoRepository cargoRepository;
19
-
20 19
   private static final Log LOG = LogFactory.getLog(ReadyToClaimNotfier.class);
21 20
 
22 21
   public ReadyToClaimNotfier(final CargoRepository cargoRepository) {
@@ -26,7 +25,7 @@ public class ReadyToClaimNotfier {
26 25
   @Transactional
27 26
   public void alertIfReadyToClaim(final TrackingId trackingId) {
28 27
     final Cargo cargo = cargoRepository.find(trackingId);
29
-
28
+                                                      
30 29
     if (cargo.delivery().isUnloadedAtDestination()) {
31 30
       LOG.info("Cargo " + cargo + " is ready to be claimed");
32 31
     }

+ 1
- 1
dddsample/src/main/java/se/citerus/dddsample/application/impl/BookingServiceImpl.java Visa fil

@@ -86,7 +86,7 @@ public final class BookingServiceImpl implements BookingService {
86 86
 
87 87
   @Override
88 88
   @Transactional
89
-  public Cargo loadCargoForRouting(TrackingId trackingId) {
89
+  public Cargo loadCargoForRouting(final TrackingId trackingId) {
90 90
     // TODO locking semantics
91 91
     final Cargo cargo = cargoRepository.find(trackingId);
92 92
     return cargo;

+ 8
- 7
dddsample/src/main/java/se/citerus/dddsample/application/util/SampleDataGenerator.java Visa fil

@@ -12,7 +12,10 @@ import org.springframework.web.context.WebApplicationContext;
12 12
 import org.springframework.web.context.support.WebApplicationContextUtils;
13 13
 import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
14 14
 import se.citerus.dddsample.domain.model.cargo.*;
15
-import se.citerus.dddsample.domain.model.handling.*;
15
+import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
16
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
17
+import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
18
+import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
16 19
 import se.citerus.dddsample.domain.model.location.Location;
17 20
 import se.citerus.dddsample.domain.model.location.LocationRepository;
18 21
 import se.citerus.dddsample.domain.model.location.SampleLocations;
@@ -250,9 +253,8 @@ public class SampleDataGenerator implements ServletContextListener {
250 253
           throw new RuntimeException(e);
251 254
         }
252 255
 
253
-        HandlingHistory handlingHistory = handlingEventRepository.lookupHandlingHistoryOfCargo(abc123);
254
-        abc123.deriveDeliveryProgress(handlingHistory);
255
-
256
+        final HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(abc123);
257
+        abc123.handled(handlingEvent.handlingActivity());
256 258
         session.update(abc123);
257 259
 
258 260
         // Cargo JKL567
@@ -295,9 +297,8 @@ public class SampleDataGenerator implements ServletContextListener {
295 297
           throw new RuntimeException(e);
296 298
         }
297 299
 
298
-        HandlingHistory handlingHistory1 = handlingEventRepository.lookupHandlingHistoryOfCargo(jkl567);
299
-        jkl567.deriveDeliveryProgress(handlingHistory1);
300
-
300
+        HandlingEvent handlingEvent1 = handlingEventRepository.mostRecentHandling(jkl567);
301
+        jkl567.handled(handlingEvent1.handlingActivity());
301 302
         session.update(jkl567);
302 303
       }
303 304
     });

+ 9
- 1
dddsample/src/main/java/se/citerus/dddsample/domain/model/cargo/CargoRepository.java Visa fil

@@ -1,5 +1,7 @@
1 1
 package se.citerus.dddsample.domain.model.cargo;
2 2
 
3
+import se.citerus.dddsample.domain.model.voyage.Voyage;
4
+
3 5
 import java.util.List;
4 6
 
5 7
 public interface CargoRepository {
@@ -13,6 +15,13 @@ public interface CargoRepository {
13 15
   Cargo find(TrackingId trackingId);
14 16
 
15 17
   /**
18
+   *
19
+   * @param voyage
20
+   * @return
21
+   */
22
+  List<Cargo> findCargosOnVoyage(Voyage voyage);
23
+
24
+  /**
16 25
    * Finds all cargo.
17 26
    *
18 27
    * @return All cargo.
@@ -30,5 +39,4 @@ public interface CargoRepository {
30 39
    * @return A unique, generated tracking Id.
31 40
    */
32 41
   TrackingId nextTrackingId();
33
-
34 42
 }

+ 7
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/model/handling/HandlingEventRepository.java Visa fil

@@ -21,4 +21,11 @@ public interface HandlingEventRepository {
21 21
    */
22 22
   HandlingHistory lookupHandlingHistoryOfCargo(Cargo cargo);
23 23
 
24
+  /**
25
+   *
26
+   * @param cargo cargo
27
+   * @return The most recent handling of the cargo, or null if it has never been handled.
28
+   */
29
+  HandlingEvent mostRecentHandling(Cargo cargo);
30
+
24 31
 }

+ 13
- 1
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryHibernate.java Visa fil

@@ -4,6 +4,7 @@ import org.springframework.stereotype.Repository;
4 4
 import se.citerus.dddsample.domain.model.cargo.Cargo;
5 5
 import se.citerus.dddsample.domain.model.cargo.CargoRepository;
6 6
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
7
+import se.citerus.dddsample.domain.model.voyage.Voyage;
7 8
 
8 9
 import java.util.List;
9 10
 import java.util.UUID;
@@ -21,6 +22,17 @@ public class CargoRepositoryHibernate extends HibernateRepository implements Car
21 22
       uniqueResult();
22 23
   }
23 24
 
25
+  @SuppressWarnings("unchecked")
26
+  @Override
27
+  public List<Cargo> findCargosOnVoyage(Voyage voyage) {
28
+    return getSession().createQuery(
29
+      "select cargo from Cargo as cargo " +
30
+        "left join cargo.itinerary.legs as leg " +
31
+        "where leg.voyage = :voyage").
32
+      setParameter("voyage", voyage).
33
+      list();
34
+  }
35
+
24 36
   public void store(Cargo cargo) {
25 37
     getSession().saveOrUpdate(cargo);
26 38
     // Delete-orphan does not seem to work correctly when the parent is a component
@@ -37,7 +49,7 @@ public class CargoRepositoryHibernate extends HibernateRepository implements Car
37 49
 
38 50
   @SuppressWarnings("unchecked")
39 51
   public List<Cargo> findAll() {
40
-      return getSession().createQuery("from Cargo").list();
52
+    return getSession().createQuery("from Cargo").list();
41 53
   }
42 54
 
43 55
 }

+ 9
- 0
dddsample/src/main/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryHibernate.java Visa fil

@@ -33,4 +33,13 @@ public class HandlingEventRepositoryHibernate extends HibernateRepository implem
33 33
     }
34 34
   }
35 35
 
36
+  @Override
37
+  public HandlingEvent mostRecentHandling(Cargo cargo) {
38
+      return (HandlingEvent) getSession().createQuery(
39
+        "from HandlingEvent where cargo = :cargo order by completionTime desc").
40
+        setParameter("cargo", cargo).
41
+        setMaxResults(1).
42
+        uniqueResult();
43
+  }
44
+
36 45
 }

+ 18
- 0
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/CargoRepositoryTest.java Visa fil

@@ -170,4 +170,22 @@ public class CargoRepositoryTest extends AbstractRepositoryTest {
170 170
     assertFalse(trackingId.equals(trackingId2));
171 171
   }
172 172
 
173
+  public void testFindCargosOnVoyage() {
174
+    Voyage voyage = voyageRepository.find(new VoyageNumber("0101"));
175
+    List<Cargo> cargos = cargoRepository.findCargosOnVoyage(voyage);
176
+    assertEquals(3, cargos.size());
177
+    for (Cargo cargo : cargos) {
178
+      boolean found = false;
179
+      for (Leg leg : cargo.itinerary().legs()) {
180
+        if (leg.voyage().sameIdentityAs(voyage)) {
181
+          found = true;
182
+        }
183
+      }
184
+      assertTrue("Cargo " + cargo + " has no leg on voyage " + voyage, found);
185
+    }
186
+    
187
+    Voyage voyage2 = voyageRepository.find(new VoyageNumber("0100S"));
188
+    assertTrue(cargoRepository.findCargosOnVoyage(voyage2).isEmpty());
189
+  }
190
+
173 191
 }

+ 11
- 0
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/hibernate/HandlingEventRepositoryTest.java Visa fil

@@ -1,5 +1,6 @@
1 1
 package se.citerus.dddsample.infrastructure.persistence.hibernate;
2 2
 
3
+import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
3 4
 import se.citerus.dddsample.domain.model.cargo.Cargo;
4 5
 import se.citerus.dddsample.domain.model.cargo.CargoRepository;
5 6
 import se.citerus.dddsample.domain.model.cargo.TrackingId;
@@ -7,7 +8,9 @@ import se.citerus.dddsample.domain.model.handling.HandlingEvent;
7 8
 import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
8 9
 import se.citerus.dddsample.domain.model.location.Location;
9 10
 import se.citerus.dddsample.domain.model.location.LocationRepository;
11
+import static se.citerus.dddsample.domain.model.location.SampleLocations.MELBOURNE;
10 12
 import se.citerus.dddsample.domain.model.location.UnLocode;
13
+import se.citerus.dddsample.domain.model.shared.HandlingActivity;
11 14
 
12 15
 import java.util.Date;
13 16
 import java.util.List;
@@ -57,4 +60,12 @@ public class HandlingEventRepositoryTest extends AbstractRepositoryTest {
57 60
     assertEquals(12, handlingEvents.size());
58 61
   }
59 62
 
63
+  public void testMostRecentHandling() {
64
+    Cargo cargo = cargoRepository.find(new TrackingId("XYZ"));
65
+    HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
66
+    assertEquals(cargo, handlingEvent.cargo());
67
+    assertEquals(toDate("2007-09-27", "05:00"), handlingEvent.completionTime());
68
+    assertEquals(new HandlingActivity(HandlingEvent.Type.CLAIM, MELBOURNE), handlingEvent.handlingActivity());
69
+  }
70
+
60 71
 }

+ 16
- 1
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/CargoRepositoryInMem.java Visa fil

@@ -7,6 +7,7 @@ import se.citerus.dddsample.domain.model.cargo.TrackingId;
7 7
 import se.citerus.dddsample.domain.model.handling.HandlingHistory;
8 8
 import se.citerus.dddsample.domain.model.location.Location;
9 9
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
10
+import se.citerus.dddsample.domain.model.voyage.Voyage;
10 11
 
11 12
 import java.util.*;
12 13
 
@@ -33,10 +34,23 @@ public class CargoRepositoryInMem implements CargoRepository {
33 34
     return cargoDb.get(trackingId.stringValue());
34 35
   }
35 36
 
36
-  public void store(final Cargo cargo) {
37
+  @Override
38
+  public List<Cargo> findCargosOnVoyage(Voyage voyage) {
39
+    List<Cargo> onVoyage = new ArrayList<Cargo>();
40
+    for (Cargo cargo : cargoDb.values()) {
41
+      if (voyage.sameIdentityAs(cargo.delivery().currentVoyage())) {
42
+        onVoyage.add(cargo);
43
+      }
44
+    }
45
+    return onVoyage;
46
+  }
47
+
48
+  @Override
49
+  public void store(Cargo cargo) {
37 50
     cargoDb.put(cargo.trackingId().stringValue(), cargo);
38 51
   }
39 52
 
53
+  @Override
40 54
   public TrackingId nextTrackingId() {
41 55
     String random = UUID.randomUUID().toString().toUpperCase();
42 56
     return new TrackingId(
@@ -44,6 +58,7 @@ public class CargoRepositoryInMem implements CargoRepository {
44 58
     );
45 59
   }
46 60
 
61
+  @Override
47 62
   public List<Cargo> findAll() {
48 63
     return new ArrayList<Cargo>(cargoDb.values());
49 64
   }

+ 19
- 4
dddsample/src/test/java/se/citerus/dddsample/infrastructure/persistence/inmemory/HandlingEventRepositoryInMem.java Visa fil

@@ -6,14 +6,18 @@ 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 8
 
9
-import java.util.ArrayList;
10
-import java.util.HashMap;
11
-import java.util.List;
12
-import java.util.Map;
9
+import java.util.*;
13 10
 
14 11
 public class HandlingEventRepositoryInMem implements HandlingEventRepository {
15 12
 
16 13
   private Map<TrackingId, List<HandlingEvent>> eventMap = new HashMap<TrackingId, List<HandlingEvent>>();
14
+  private static final Comparator<HandlingEvent> BY_COMPLETION_TIME_DESC = new Comparator<HandlingEvent>() {
15
+    @Override
16
+    public int compare(HandlingEvent o1, HandlingEvent o2) {
17
+      // Newest first
18
+      return o2.completionTime().compareTo(o1.completionTime());
19
+    }
20
+  };
17 21
 
18 22
   @Override
19 23
   public void store(HandlingEvent event) {
@@ -37,4 +41,15 @@ public class HandlingEventRepositoryInMem implements HandlingEventRepository {
37 41
     }
38 42
   }
39 43
 
44
+  @Override
45
+  public HandlingEvent mostRecentHandling(Cargo cargo) {
46
+    List<HandlingEvent> handlingEvents = eventMap.get(cargo.trackingId());
47
+    if (handlingEvents == null) {
48
+      return null;
49
+    }
50
+
51
+    Collections.sort(handlingEvents, BY_COMPLETION_TIME_DESC);
52
+    return handlingEvents.get(0);
53
+  }
54
+
40 55
 }

+ 11
- 4
dddsample/src/test/java/se/citerus/dddsample/scenario/CargoLifecycleScenarioTest.java Visa fil

@@ -8,8 +8,11 @@ import static se.citerus.dddsample.application.util.DateTestUtil.toDate;
8 8
 import se.citerus.dddsample.domain.model.cargo.*;
9 9
 import static se.citerus.dddsample.domain.model.cargo.RoutingStatus.*;
10 10
 import static se.citerus.dddsample.domain.model.cargo.TransportStatus.*;
11
-import se.citerus.dddsample.domain.model.handling.*;
11
+import se.citerus.dddsample.domain.model.handling.CannotCreateHandlingEventException;
12
+import se.citerus.dddsample.domain.model.handling.HandlingEvent;
12 13
 import static se.citerus.dddsample.domain.model.handling.HandlingEvent.Type.*;
14
+import se.citerus.dddsample.domain.model.handling.HandlingEventFactory;
15
+import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
13 16
 import se.citerus.dddsample.domain.model.location.Location;
14 17
 import se.citerus.dddsample.domain.model.location.LocationRepository;
15 18
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
@@ -375,16 +378,20 @@ public class CargoLifecycleScenarioTest {
375 378
   }
376 379
 
377 380
   private void createHandlingEventAndUpdateAggregates(Date completionTime, Voyage voyage, Location location, HandlingEvent.Type type) throws CannotCreateHandlingEventException {
381
+    updateHandlingEventAggregate(completionTime, voyage, location, type);
382
+    updateCargoAggregate();
383
+  }
384
+
385
+  private void updateHandlingEventAggregate(Date completionTime, Voyage voyage, Location location, HandlingEvent.Type type) throws CannotCreateHandlingEventException {
378 386
     VoyageNumber voyageNumber = voyage != null ? voyage.voyageNumber() : null;
379 387
     HandlingEvent handlingEvent = handlingEventFactory.createHandlingEvent(new Date(), completionTime, trackingId, voyageNumber, location.unLocode(), type);
380 388
     handlingEventRepository.store(handlingEvent);
381
-    updateCargoAggregate();
382 389
   }
383 390
 
384 391
   private void updateCargoAggregate() {
385 392
     Cargo cargo = cargoRepository.find(trackingId);
386
-    HandlingHistory handlingHistory = handlingEventRepository.lookupHandlingHistoryOfCargo(cargo);
387
-    cargo.deriveDeliveryProgress(handlingHistory);
393
+    HandlingEvent handlingEvent = handlingEventRepository.mostRecentHandling(cargo);
394
+    cargo.handled(handlingEvent.handlingActivity());
388 395
     cargoRepository.store(cargo);
389 396
   }
390 397