Bläddra i källkod

Moved RoutingService interface back into domain layer, and moved ExternalRoutingService to infrastructure.

peter_backlund 18 år sedan
förälder
incheckning
80173f8775

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

@@ -5,11 +5,11 @@ 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.RoutingService;
9 8
 import se.citerus.dddsample.domain.model.cargo.*;
10 9
 import se.citerus.dddsample.domain.model.location.Location;
11 10
 import se.citerus.dddsample.domain.model.location.LocationRepository;
12 11
 import se.citerus.dddsample.domain.model.location.UnLocode;
12
+import se.citerus.dddsample.domain.service.RoutingService;
13 13
 
14 14
 import java.util.Date;
15 15
 import java.util.List;

+ 20
- 0
dddsample/src/main/java/se/citerus/dddsample/domain/service/RoutingService.java Visa fil

@@ -0,0 +1,20 @@
1
+package se.citerus.dddsample.domain.service;
2
+
3
+import se.citerus.dddsample.domain.model.cargo.Itinerary;
4
+import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
5
+
6
+import java.util.List;
7
+
8
+/**
9
+ * Routing service.
10
+ *
11
+ */
12
+public interface RoutingService {
13
+
14
+  /**
15
+   * @param routeSpecification route specification
16
+   * @return A list of itineraries that satisfy the specification. May be an empty list if no route is found.
17
+   */
18
+  List<Itinerary> fetchRoutesForSpecification(RouteSpecification routeSpecification);
19
+
20
+}

+ 84
- 0
dddsample/src/main/java/se/citerus/dddsample/infrastructure/routing/ExternalRoutingService.java Visa fil

@@ -0,0 +1,84 @@
1
+package se.citerus.dddsample.infrastructure.routing;
2
+
3
+import org.springframework.transaction.annotation.Transactional;
4
+import se.citerus.dddsample.domain.model.cargo.Itinerary;
5
+import se.citerus.dddsample.domain.model.cargo.Leg;
6
+import se.citerus.dddsample.domain.model.cargo.RouteSpecification;
7
+import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
8
+import se.citerus.dddsample.domain.model.carrier.VoyageRepository;
9
+import se.citerus.dddsample.domain.model.location.Location;
10
+import se.citerus.dddsample.domain.model.location.LocationRepository;
11
+import se.citerus.dddsample.domain.model.location.UnLocode;
12
+import se.citerus.dddsample.domain.service.RoutingService;
13
+import se.citerus.routingteam.GraphTraversalService;
14
+import se.citerus.routingteam.TransitEdge;
15
+import se.citerus.routingteam.TransitPath;
16
+
17
+import java.util.ArrayList;
18
+import java.util.Date;
19
+import java.util.List;
20
+
21
+/**
22
+ * Our end of the routing service. This is basically a data model
23
+ * translation layer between our domain model and the API put forward
24
+ * by the routing team, which operates in a different context from us.
25
+ *
26
+ */
27
+public class ExternalRoutingService implements RoutingService {
28
+
29
+  private GraphTraversalService graphTraversalService;
30
+  private LocationRepository locationRepository;
31
+  private VoyageRepository voyageRepository;
32
+
33
+  @Transactional(readOnly = true)
34
+  public List<Itinerary> fetchRoutesForSpecification(RouteSpecification routeSpecification) {
35
+    final Location origin = routeSpecification.origin();
36
+    final Location destination = routeSpecification.destination();
37
+
38
+    final List<TransitPath> transitPaths = graphTraversalService.findShortestPath(
39
+      origin.unLocode().idString(),
40
+      destination.unLocode().idString()
41
+    );
42
+    
43
+    final List<Itinerary> itineraries = new ArrayList<Itinerary>();
44
+
45
+    for (TransitPath transitPath : transitPaths) {
46
+      final Itinerary itinerary = toItinerary(transitPath);
47
+      // Use the specification to safe-guard against invalid itineraries
48
+      if (routeSpecification.isSatisfiedBy(itinerary)) {
49
+        itineraries.add(itinerary);
50
+      }
51
+    }
52
+
53
+    return itineraries;
54
+  }
55
+
56
+  private Itinerary toItinerary(TransitPath transitPath) {
57
+    List<Leg> legs = new ArrayList(transitPath.getTransitEdges().size());
58
+    for (TransitEdge edge : transitPath.getTransitEdges()) {
59
+      legs.add(toLeg(edge));
60
+    }
61
+    return new Itinerary(legs);
62
+  }
63
+
64
+  private Leg toLeg(TransitEdge edge) {
65
+    return new Leg(
66
+      voyageRepository.find(new VoyageNumber(edge.getCarrierMovementId())),
67
+      locationRepository.find(new UnLocode(edge.getFromUnLocode())),
68
+      locationRepository.find(new UnLocode(edge.getToUnLocode())),
69
+      new Date(), new Date()  // TODO better dates
70
+    );
71
+  }
72
+
73
+  public void setGraphTraversalService(GraphTraversalService graphTraversalService) {
74
+    this.graphTraversalService = graphTraversalService;
75
+  }
76
+
77
+  public void setLocationRepository(LocationRepository locationRepository) {
78
+    this.locationRepository = locationRepository;
79
+  }
80
+
81
+  public void setVoyageRepository(VoyageRepository voyageRepository) {
82
+    this.voyageRepository = voyageRepository;
83
+  }
84
+}

+ 1
- 1
dddsample/src/main/resources/context-service.xml Visa fil

@@ -32,7 +32,7 @@
32 32
 
33 33
   <tx:annotation-driven transaction-manager="transactionManager"/>
34 34
 
35
-  <bean id="routingService" class="se.citerus.dddsample.application.impl.RoutingServiceImpl">
35
+  <bean id="routingService" class="se.citerus.dddsample.infrastructure.routing.ExternalRoutingService">
36 36
     <property name="locationRepository" ref="locationRepository"/>
37 37
     <property name="voyageRepository" ref="voyageRepository"/>
38 38
     <property name="graphTraversalService" ref="graphTraversalService"/>

+ 1
- 0
dddsample/src/test/java/se/citerus/dddsample/application/BookingServiceTest.java Visa fil

@@ -10,6 +10,7 @@ import se.citerus.dddsample.domain.model.location.LocationRepository;
10 10
 import static se.citerus.dddsample.domain.model.location.SampleLocations.CHICAGO;
11 11
 import static se.citerus.dddsample.domain.model.location.SampleLocations.STOCKHOLM;
12 12
 import se.citerus.dddsample.domain.model.location.UnLocode;
13
+import se.citerus.dddsample.domain.service.RoutingService;
13 14
 
14 15
 public class BookingServiceTest extends TestCase {
15 16
 

+ 7
- 7
dddsample/src/test/java/se/citerus/dddsample/application/RoutingServiceTest.java Visa fil

@@ -2,7 +2,6 @@ package se.citerus.dddsample.application;
2 2
 
3 3
 import junit.framework.TestCase;
4 4
 import static org.easymock.EasyMock.*;
5
-import se.citerus.dddsample.application.impl.RoutingServiceImpl;
6 5
 import se.citerus.dddsample.domain.model.cargo.*;
7 6
 import se.citerus.dddsample.domain.model.carrier.SampleVoyages;
8 7
 import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
@@ -11,6 +10,7 @@ import se.citerus.dddsample.domain.model.location.Location;
11 10
 import se.citerus.dddsample.domain.model.location.LocationRepository;
12 11
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
13 12
 import se.citerus.dddsample.infrastructure.persistence.inmemory.LocationRepositoryInMem;
13
+import se.citerus.dddsample.infrastructure.routing.ExternalRoutingService;
14 14
 import se.citerus.routingteam.GraphTraversalService;
15 15
 import se.citerus.routingteam.internal.GraphDAO;
16 16
 import se.citerus.routingteam.internal.GraphTraversalServiceImpl;
@@ -22,16 +22,16 @@ import java.util.List;
22 22
 
23 23
 public class RoutingServiceTest extends TestCase {
24 24
 
25
-  private RoutingServiceImpl routingServiceImpl;
25
+  private ExternalRoutingService externalRoutingService;
26 26
   private VoyageRepository voyageRepository;
27 27
 
28 28
   protected void setUp() throws Exception {
29
-    routingServiceImpl = new RoutingServiceImpl();
29
+    externalRoutingService = new ExternalRoutingService();
30 30
     LocationRepository locationRepository = new LocationRepositoryInMem();
31
-    routingServiceImpl.setLocationRepository(locationRepository);
31
+    externalRoutingService.setLocationRepository(locationRepository);
32 32
 
33 33
     voyageRepository = createMock(VoyageRepository.class);
34
-    routingServiceImpl.setVoyageRepository(voyageRepository);
34
+    externalRoutingService.setVoyageRepository(voyageRepository);
35 35
 
36 36
     GraphTraversalService graphTraversalService = new GraphTraversalServiceImpl(new GraphDAO(createMock(DataSource.class)) {
37 37
       public List<String> listLocations() {
@@ -41,7 +41,7 @@ public class RoutingServiceTest extends TestCase {
41 41
       public void storeCarrierMovementId(String cmId, String from, String to) {
42 42
       }
43 43
     });
44
-    routingServiceImpl.setGraphTraversalService(graphTraversalService);
44
+    externalRoutingService.setGraphTraversalService(graphTraversalService);
45 45
   }
46 46
 
47 47
   public void testCalculatePossibleRoutes() {
@@ -53,7 +53,7 @@ public class RoutingServiceTest extends TestCase {
53 53
     
54 54
     replay(voyageRepository);
55 55
 
56
-    List<Itinerary> candidates = routingServiceImpl.fetchRoutesForSpecification(routeSpecification);
56
+    List<Itinerary> candidates = externalRoutingService.fetchRoutesForSpecification(routeSpecification);
57 57
     assertNotNull(candidates);
58 58
     
59 59
     for (Itinerary itinerary : candidates) {

+ 79
- 0
dddsample/src/test/java/se/citerus/dddsample/infrastructure/routing/ExternalRoutingServiceTest.java Visa fil

@@ -0,0 +1,79 @@
1
+package se.citerus.dddsample.infrastructure.routing;
2
+
3
+import junit.framework.TestCase;
4
+import static org.easymock.EasyMock.*;
5
+import se.citerus.dddsample.domain.model.cargo.*;
6
+import se.citerus.dddsample.domain.model.carrier.SampleVoyages;
7
+import se.citerus.dddsample.domain.model.carrier.VoyageNumber;
8
+import se.citerus.dddsample.domain.model.carrier.VoyageRepository;
9
+import se.citerus.dddsample.domain.model.location.Location;
10
+import se.citerus.dddsample.domain.model.location.LocationRepository;
11
+import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
12
+import se.citerus.dddsample.infrastructure.persistence.inmemory.LocationRepositoryInMem;
13
+import se.citerus.routingteam.GraphTraversalService;
14
+import se.citerus.routingteam.internal.GraphDAO;
15
+import se.citerus.routingteam.internal.GraphTraversalServiceImpl;
16
+
17
+import javax.sql.DataSource;
18
+import java.util.Arrays;
19
+import java.util.Date;
20
+import java.util.List;
21
+
22
+public class ExternalRoutingServiceTest extends TestCase {
23
+
24
+  private ExternalRoutingService externalRoutingService;
25
+  private VoyageRepository voyageRepository;
26
+
27
+  protected void setUp() throws Exception {
28
+    externalRoutingService = new ExternalRoutingService();
29
+    LocationRepository locationRepository = new LocationRepositoryInMem();
30
+    externalRoutingService.setLocationRepository(locationRepository);
31
+
32
+    voyageRepository = createMock(VoyageRepository.class);
33
+    externalRoutingService.setVoyageRepository(voyageRepository);
34
+
35
+    GraphTraversalService graphTraversalService = new GraphTraversalServiceImpl(new GraphDAO(createMock(DataSource.class)) {
36
+      public List<String> listLocations() {
37
+        return Arrays.asList(TOKYO.unLocode().idString(), STOCKHOLM.unLocode().idString(), GOTHENBURG.unLocode().idString());
38
+      }
39
+
40
+      public void storeCarrierMovementId(String cmId, String from, String to) {
41
+      }
42
+    });
43
+    externalRoutingService.setGraphTraversalService(graphTraversalService);
44
+  }
45
+
46
+  public void testCalculatePossibleRoutes() {
47
+    TrackingId trackingId = new TrackingId("ABC");
48
+    Cargo cargo = new Cargo(trackingId, HONGKONG, HELSINKI);
49
+    RouteSpecification routeSpecification = new RouteSpecification(cargo.origin(), cargo.destination(), new Date());
50
+
51
+    expect(voyageRepository.find(isA(VoyageNumber.class))).andStubReturn(SampleVoyages.CM002);
52
+    
53
+    replay(voyageRepository);
54
+
55
+    List<Itinerary> candidates = externalRoutingService.fetchRoutesForSpecification(routeSpecification);
56
+    assertNotNull(candidates);
57
+    
58
+    for (Itinerary itinerary : candidates) {
59
+      List<Leg> legs = itinerary.legs();
60
+      assertNotNull(legs);
61
+      assertFalse(legs.isEmpty());
62
+
63
+      // Cargo origin and start of first leg should match
64
+      assertEquals(cargo.origin(), legs.get(0).loadLocation());
65
+
66
+      // Cargo final destination and last leg stop should match
67
+      Location lastLegStop = legs.get(legs.size() - 1).unloadLocation();
68
+      assertEquals(cargo.destination(), lastLegStop);
69
+
70
+      for (int i = 0; i < legs.size() - 1; i++) {
71
+        // Assert that all legs are connected
72
+        assertEquals(legs.get(i).unloadLocation(), legs.get(i + 1).loadLocation());
73
+      }
74
+    }
75
+
76
+    verify(voyageRepository);
77
+  }
78
+
79
+}

+ 5
- 1
dddsample/src/test/java/se/citerus/dddsample/scenario/CargoHandlingScenarioTest.java Visa fil

@@ -1,7 +1,10 @@
1 1
 package se.citerus.dddsample.scenario;
2 2
 
3 3
 import junit.framework.TestCase;
4
-import se.citerus.dddsample.application.*;
4
+import se.citerus.dddsample.application.HandlingEventRegistrationAttempt;
5
+import se.citerus.dddsample.application.HandlingEventService;
6
+import se.citerus.dddsample.application.SystemEvents;
7
+import se.citerus.dddsample.application.TrackingService;
5 8
 import se.citerus.dddsample.application.impl.HandlingEventServiceImpl;
6 9
 import se.citerus.dddsample.application.impl.TrackingServiceImpl;
7 10
 import se.citerus.dddsample.domain.model.cargo.*;
@@ -15,6 +18,7 @@ import se.citerus.dddsample.domain.model.handling.HandlingEventRepository;
15 18
 import se.citerus.dddsample.domain.model.location.Location;
16 19
 import se.citerus.dddsample.domain.model.location.LocationRepository;
17 20
 import static se.citerus.dddsample.domain.model.location.SampleLocations.*;
21
+import se.citerus.dddsample.domain.service.RoutingService;
18 22
 import se.citerus.dddsample.infrastructure.persistence.inmemory.LocationRepositoryInMem;
19 23
 import se.citerus.dddsample.infrastructure.persistence.inmemory.VoyageRepositoryInMem;
20 24