Skip to content

Commit

Permalink
finished all
Browse files Browse the repository at this point in the history
  • Loading branch information
upangka committed Dec 22, 2023
1 parent f592f98 commit b97d94a
Show file tree
Hide file tree
Showing 101 changed files with 23,485 additions and 2 deletions.
11 changes: 11 additions & 0 deletions ddd/springboot-dddsample/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,21 @@
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.18</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.hzz.ddd;

import org.hzz.pathfinder.config.PathfinderApplicationContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Import;

@SpringBootApplication
@Import({PathfinderApplicationContext.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.hzz.ddd.application;

import org.hzz.ddd.domain.model.cargo.Itinerary;
import org.hzz.ddd.domain.model.cargo.TrackingId;
import org.hzz.ddd.domain.model.location.UnLocode;

import java.time.Instant;
import java.util.List;

/**
* Cargo booking service.
*/
public interface BookingService {
/**
* Registers a new cargo in the tracking system, not yet routed.
*
* @param origin cargo origin
* @param destination cargo destination
* @param arrivalDeadline arrival deadline
* @return Cargo tracking id
*/
TrackingId bookNewCargo(UnLocode origin, UnLocode destination, Instant arrivalDeadline);

/**
* Requests a list of itineraries describing possible routes for this cargo.
*
* @param trackingId cargo tracking id
* @return A list of possible itineraries for this cargo
*/
List<Itinerary> requestPossibleRoutesForCargo(TrackingId trackingId);

/**
* @param itinerary itinerary describing the selected route
* @param trackingId cargo tracking id
*/
void assignCargoToRoute(Itinerary itinerary, TrackingId trackingId);

/**
* Changes the destination of a cargo.
*
* @param trackingId cargo tracking id
* @param unLocode UN locode of new destination
*/
void changeDestination(TrackingId trackingId, UnLocode unLocode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.hzz.ddd.application.impl;

import lombok.extern.slf4j.Slf4j;
import org.hzz.ddd.application.BookingService;
import org.hzz.ddd.domain.model.cargo.CargoFactory;
import org.hzz.ddd.domain.model.cargo.CargoRepository;
import org.hzz.ddd.domain.model.cargo.Itinerary;
import org.hzz.ddd.domain.model.cargo.TrackingId;
import org.hzz.ddd.domain.model.location.LocationRepository;
import org.hzz.ddd.domain.model.location.UnLocode;
import org.hzz.ddd.domain.service.RoutingService;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.List;

@Slf4j
@Service
public class BookingServiceImpl implements BookingService {

private final CargoRepository cargoRepository;
private final LocationRepository locationRepository;
private final RoutingService routingService;
private final CargoFactory cargoFactory;

public BookingServiceImpl(final CargoRepository cargoRepository,
final LocationRepository locationRepository,
final RoutingService routingService,
final CargoFactory cargoFactory) {
this.cargoRepository = cargoRepository;
this.locationRepository = locationRepository;
this.routingService = routingService;
this.cargoFactory = cargoFactory;
}

@Override
public TrackingId bookNewCargo(UnLocode origin, UnLocode destination, Instant arrivalDeadline) {
return null;
}

@Override
public List<Itinerary> requestPossibleRoutesForCargo(TrackingId trackingId) {
return null;
}

@Override
public void assignCargoToRoute(Itinerary itinerary, TrackingId trackingId) {

}

@Override
public void changeDestination(TrackingId trackingId, UnLocode unLocode) {

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.hzz.ddd.application.util;

import java.time.Instant;
import java.time.format.DateTimeParseException;

/**
* A few utils for working with Date in tests.
*
*/
public final class DateUtils {

/**
* @param date date string as yyyy-MM-dd
* @return Date representation
*/
public static Instant toDate(final String date) {
return toDate(date, "00:00");
}

/**
* @param date date string as yyyy-MM-dd
* @param time time string as HH:mm
* @return Date representation
*/
public static Instant toDate(final String date, final String time) {
try {
return Instant.parse(date + "T" + time + ":00Z");
} catch (DateTimeParseException e) {
throw new RuntimeException(e);
}
}

/**
* Prevent instantiation.
*/
private DateUtils() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.hzz.ddd.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class DDDSampleApplicationContext {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package org.hzz.ddd.domain.model.cargo;


import org.apache.commons.lang3.Validate;
import org.hzz.ddd.domain.model.handling.HandlingHistory;
import org.hzz.ddd.domain.model.location.Location;
import org.hzz.ddd.domain.shared.Entity;

import javax.persistence.*;
import java.util.List;

@javax.persistence.Entity(name="Cargo")
@Table(name = "Cargo")
public class Cargo implements Entity<Cargo> {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public long id;

@Column(name = "tracking_id", unique = true)
public String trackingId;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "origin_id")
public Location origin;

@Embedded
public RouteSpecification routeSpecification;

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn(name = "cargo_id")
public List<Leg> itinerary; // TODO figure out if we can map an Itinerary object instead

@Embedded
public Delivery delivery;

public Cargo() {
// Needed by Hibernate
}

public Cargo(final TrackingId trackingId, final RouteSpecification routeSpecification) {
Validate.notNull(trackingId, "Tracking ID is required");
Validate.notNull(routeSpecification, "Route specification is required");

this.trackingId = trackingId.idString();
// Cargo origin never changes, even if the route specification changes.
// However, at creation, cargo origin can be derived from the initial route specification.
this.origin = routeSpecification.origin();
this.routeSpecification = routeSpecification;

this.delivery = Delivery.derivedFrom(
this.routeSpecification, null, HandlingHistory.EMPTY
);
}

public Cargo(TrackingId trackingId, RouteSpecification routeSpecification, Itinerary itinerary) {
Validate.notNull(trackingId, "Tracking ID is required");
Validate.notNull(routeSpecification, "Route specification is required");
this.trackingId = trackingId.idString();
this.origin = routeSpecification.origin();
this.routeSpecification = routeSpecification;
this.itinerary = itinerary.legs();

this.delivery = Delivery.derivedFrom(
this.routeSpecification, new Itinerary(this.itinerary), HandlingHistory.EMPTY
);
}

/**
* The tracking id is the identity of this entity, and is unique.
*
* @return Tracking id.
*/
public TrackingId trackingId() {
return new TrackingId(trackingId);
}

/**
* @return Origin location.
*/
public Location origin() {
return origin;
}

/**
* @return The delivery. Never null.
*/
public Delivery delivery() {
return delivery;
}

/**
* @return The itinerary. Never null.
*/
public Itinerary itinerary() {
if (itinerary == null || itinerary.isEmpty()) {
return Itinerary.EMPTY_ITINERARY;
}
return new Itinerary(itinerary);
}

/**
* @return The route specification.
*/
public RouteSpecification routeSpecification() {
return routeSpecification;
}

/**
* Specifies a new route for this cargo.
*
* @param routeSpecification route specification.
*/
public void specifyNewRoute(final RouteSpecification routeSpecification) {
Validate.notNull(routeSpecification, "Route specification is required");

this.routeSpecification = routeSpecification;
Itinerary itineraryForRouting = this.itinerary != null && !this.itinerary.isEmpty() ? new Itinerary(this.itinerary) : null;
// Handling consistency within the Cargo aggregate synchronously
this.delivery = delivery.updateOnRouting(this.routeSpecification, itineraryForRouting);
}

/**
* Attach a new itinerary to this cargo.
*
* @param itinerary an itinerary. May not be null.
*/
public void assignToRoute(final Itinerary itinerary) {
Validate.notNull(itinerary, "Itinerary is required for assignment");

this.itinerary = itinerary.legs();
// Handling consistency within the Cargo aggregate synchronously
this.delivery = delivery.updateOnRouting(this.routeSpecification, itinerary);
}

/**
* Updates all aspects of the cargo aggregate status
* based on the current route specification, itinerary and handling of the cargo.
* <p/>
* When either of those three changes, i.e. when a new route is specified for the cargo,
* the cargo is assigned to a route or when the cargo is handled, the status must be
* re-calculated.
* <p/>
* {@link RouteSpecification} and {@link Itinerary} are both inside the Cargo
* aggregate, so changes to them cause the status to be updated <b>synchronously</b>,
* but changes to the delivery history (when a cargo is handled) cause the status update
* to happen <b>asynchronously</b> since {@link se.citerus.dddsample.domain.model.handling.HandlingEvent} is in a different aggregate.
*
* @param handlingHistory handling history
*/
public void deriveDeliveryProgress(final HandlingHistory handlingHistory) {
// Delivery is a value object, so we can simply discard the old one
// and replace it with a new
this.delivery = Delivery.derivedFrom(routeSpecification(), itinerary(), handlingHistory.filterOnCargo(new TrackingId(this.trackingId)));
}

@Override
public boolean sameIdentityAs(final Cargo other) {
return other != null && trackingId.equals(other.trackingId);
}

/**
* @param object to compare
* @return True if they have the same identity
* @see #sameIdentityAs(Cargo)
*/
@Override
public boolean equals(final Object object) {
if (this == object) return true;
if (object == null || getClass() != object.getClass()) return false;

final Cargo other = (Cargo) object;
return sameIdentityAs(other);
}

/**
* @return Hash code of tracking id.
*/
@Override
public int hashCode() {
return trackingId.hashCode();
}

@Override
public String toString() {
return trackingId;
}


}
Loading

0 comments on commit b97d94a

Please sign in to comment.