The Shipment & Transportation Service manages the end-to-end shipment lifecycle including carrier integration, shipment creation, tracking, and delivery confirmation. It provides multi-carrier support with extensible adapter architecture, currently supporting FedEx with the ability to add UPS, USPS, and other carriers.
Architecture Pattern: Hexagonal Architecture with Domain-Driven Design (DDD) Technology Stack: Spring Boot 3.3, Spring Data MongoDB, Spring Kafka, CloudEvents Integration Pattern: Event-Driven + Adapter Pattern for Carriers
The Shipment & Transportation bounded context is responsible for all aspects of getting packed orders to their destinations via third-party carriers.
Responsibilities (Whatβs IN):
External Dependencies (Whatβs OUT):
Core Domain Terms:
Strategic Importance: MEDIUM-HIGH - Enables final delivery to customers
This domain provides critical business value through:
Subdomains:
Description: Represents a shipment containing one or more packages being sent to a recipient via a carrier.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@AggregateRoot
public class Shipment {
private String shipmentId; // Aggregate ID
private String orderId;
private List<Package> packages;
private Address recipientAddress;
private Address senderAddress;
private Carrier carrier;
private ServiceLevel serviceLevel;
private ShipmentStatus status;
private TrackingInfo trackingInfo;
private List<TrackingEvent> trackingEvents;
private LocalDateTime createdAt;
private LocalDateTime estimatedDelivery;
private LocalDateTime actualDelivery;
private ProofOfDelivery proofOfDelivery;
// Business methods
public void addPackage(Package package_);
public void assignCarrier(Carrier carrier, ServiceLevel serviceLevel);
public void generateLabel();
public void tender();
public void updateTracking(TrackingEvent event);
public void markDelivered(ProofOfDelivery pod);
public void recordException(DeliveryException exception);
public void cancel();
// Invariants
private void ensureHasAtLeastOnePackage();
private void ensureValidAddress();
private void ensureCanBeCancelled();
}
Shipment Status State Machine:
1
2
3
CREATED β LABEL_GENERATED β TENDERED β IN_TRANSIT β OUT_FOR_DELIVERY β DELIVERED
β β
CANCELLED EXCEPTION
Invariants:
Domain Events:
ShipmentCreatedEventShipmentDispatchedEventShipmentInTransitEventShipmentOutForDeliveryEventShipmentDeliveredEventShipmentExceptionEventShipmentCancelledEventDescription: Represents a group of packages being tendered to a carrier together (for manifesting and pickup).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@AggregateRoot
public class Load {
private String loadId; // Aggregate ID
private Carrier carrier;
private List<String> shipmentIds;
private LoadStatus status;
private LocalDateTime scheduledPickupTime;
private LocalDateTime actualPickupTime;
private LocalDateTime closedAt;
private String manifestDocument;
private LoadMetrics metrics;
// Business methods
public void addShipment(String shipmentId);
public void closeLoad();
public void tender();
public void recordPickup(LocalDateTime pickupTime);
// Invariants
private void ensureAllShipmentsForSameCarrier();
private void ensureNotAlreadyClosed();
}
Domain Events:
LoadCreatedEventLoadClosedEventLoadTenderedEventLoadPickedUpEvent1
2
3
4
5
6
7
8
9
10
11
12
@Entity
public class Package {
private String packageId;
private String trackingNumber;
private Dimensions dimensions;
private Weight weight;
private List<String> skus; // Contents
private Label label;
public DimensionalWeight calculateDimensionalWeight(int dimFactor);
public boolean requiresOverweightSurcharge(Weight threshold);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Entity
public class TrackingEvent {
private String eventId;
private TrackingEventType type;
private LocalDateTime timestamp;
private String location;
private String description;
private String carrierEventCode;
public boolean isDeliveryEvent();
public boolean isExceptionEvent();
}
enum TrackingEventType {
PICKED_UP,
DEPARTED_FACILITY,
ARRIVED_FACILITY,
IN_TRANSIT,
OUT_FOR_DELIVERY,
DELIVERED,
DELIVERY_ATTEMPT_FAILED,
EXCEPTION
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@ValueObject
public class Address {
private String recipientName;
private String company;
private String addressLine1;
private String addressLine2;
private String city;
private String state;
private String postalCode;
private String country;
private AddressType type; // RESIDENTIAL, COMMERCIAL
public boolean isValid();
public boolean isInternational();
public boolean isResidential();
public String formatForLabel();
}
1
2
3
4
5
6
7
8
9
10
@ValueObject
public class TrackingInfo {
private String masterTrackingNumber;
private List<String> packageTrackingNumbers;
private ShipmentStatus currentStatus;
private String currentLocation;
private LocalDateTime lastUpdateTime;
public boolean hasUpdates(LocalDateTime since);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@ValueObject
public class RateQuote {
private Carrier carrier;
private ServiceLevel serviceLevel;
private Money totalCost;
private Money baseRate;
private List<Surcharge> surcharges;
private int transitDays;
private LocalDateTime estimatedDelivery;
public Money getTotalCost();
public boolean isCheaperThan(RateQuote other);
}
@ValueObject
public class Surcharge {
private String type; // FUEL, RESIDENTIAL, DELIVERY_AREA, etc.
private Money amount;
}
1
2
3
4
5
6
7
8
9
@ValueObject
public class ProofOfDelivery {
private LocalDateTime deliveredAt;
private String recipientName; // Who signed
private String deliveryLocation; // "Front door", "Mailroom"
private byte[] signature; // Image
private byte[] photo; // Delivery photo
private String driverNotes;
}
Responsibility: Select optimal carrier based on shipment requirements and business rules.
1
2
3
4
5
6
7
8
9
10
11
12
13
@DomainService
public class CarrierSelectionService {
public Carrier selectCarrier(
Shipment shipment,
List<RateQuote> quotes,
SelectionStrategy strategy
);
public Carrier selectForCost(List<RateQuote> quotes);
public Carrier selectForSpeed(List<RateQuote> quotes);
public boolean canCarrierShip(Carrier carrier, Shipment shipment);
}
Responsibility: Retrieve and compare rates from multiple carriers.
1
2
3
4
5
6
7
8
9
10
11
12
@DomainService
public class RateShoppingService {
public List<RateQuote> getRateQuotes(
List<Package> packages,
Address destination,
ServiceLevel serviceLevel
);
public RateQuote getLowestCostQuote(List<RateQuote> quotes);
public RateQuote getFastestQuote(List<RateQuote> quotes);
}
Responsibility: Calculate dimensional weight for rate determination.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@DomainService
public class DimensionalWeightCalculator {
public DimensionalWeight calculate(
Dimensions packageDimensions,
Carrier carrier
);
public Weight getChargeableWeight(
Weight actualWeight,
DimensionalWeight dimWeight
);
private int getDimFactor(Carrier carrier); // FedEx: 139, UPS: 139
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public interface ICarrierAdapter {
// Rating
RateQuote getRateQuote(ShipmentRequest request);
// Shipping
ShipmentResponse createShipment(ShipmentRequest request);
Label generateLabel(String shipmentId, LabelFormat format);
void voidShipment(String trackingNumber);
// Tracking
TrackingInfo getTracking(String trackingNumber);
List<TrackingEvent> getTrackingHistory(String trackingNumber);
// Address validation
AddressValidationResult validateAddress(Address address);
// Load management
ManifestResult createManifest(List<String> trackingNumbers);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
public class FedExAdapter implements ICarrierAdapter {
private final FedExApiClient apiClient;
private final FedExRequestSigner signer;
private final FedExApiProperties properties;
@Override
public RateQuote getRateQuote(ShipmentRequest request) {
// Call FedEx Rating API
FedExRateRequest fedexRequest = mapToFedExRequest(request);
FedExRateResponse response = apiClient.getRates(fedexRequest);
return mapToRateQuote(response);
}
@Override
public ShipmentResponse createShipment(ShipmentRequest request) {
// Call FedEx Shipping API
FedExShipRequest fedexRequest = mapToFedExShipRequest(request);
FedExShipResponse response = apiClient.createShipment(fedexRequest);
return mapToShipmentResponse(response);
}
// ... other methods
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Commands
public interface CreateShipmentCommand {
Shipment execute(CreateShipmentRequest request);
}
public interface GenerateLabelCommand {
Label execute(String shipmentId, LabelFormat format);
}
public interface CancelShipmentCommand {
void execute(String shipmentId);
}
public interface CreateLoadCommand {
Load execute(CreateLoadRequest request);
}
// Queries
public interface GetShipmentTrackingQuery {
TrackingInfo execute(String shipmentId);
}
public interface GetRateQuotesQuery {
List<RateQuote> execute(RateQuoteRequest request);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Repository ports
public interface ShipmentRepository {
Optional<Shipment> findById(String shipmentId);
void save(Shipment shipment);
List<Shipment> findByStatus(ShipmentStatus status);
}
public interface LoadRepository {
Optional<Load> findById(String loadId);
void save(Load load);
}
// Carrier adapter registry
public interface CarrierAdapterRegistry {
ICarrierAdapter getAdapter(Carrier carrier);
List<ICarrierAdapter> getAllAdapters();
}
// Event publishing
public interface EventPublisher {
void publish(DomainEvent event);
}
REST Controller
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@RestController
@RequestMapping("/api/v1/shipments")
public class ShipmentController {
@PostMapping
public ResponseEntity<ShipmentResponse> createShipment(
@RequestBody CreateShipmentRequest request
);
@GetMapping("/{shipmentId}/tracking")
public ResponseEntity<TrackingResponse> getTracking(
@PathVariable String shipmentId
);
@GetMapping("/{shipmentId}/label")
public ResponseEntity<byte[]> getLabel(
@PathVariable String shipmentId,
@RequestParam(defaultValue = "PDF") LabelFormat format
);
@PostMapping("/{shipmentId}/cancel")
public ResponseEntity<Void> cancelShipment(@PathVariable String shipmentId);
@PostMapping("/rate-quotes")
public ResponseEntity<List<RateQuoteResponse>> getRateQuotes(
@RequestBody RateQuoteRequest request
);
}
Event Listener
1
2
3
4
5
6
7
8
@Component
public class WarehouseEventListener {
@KafkaListener(topics = "fulfillment.warehouse.v1.events")
public void handlePackingCompleted(PackingCompletedEvent event) {
// Create shipment for packed order
}
}
Tracking Update Job
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Component
@Scheduled(fixedDelay = 3600000) // Every hour
public class TrackingUpdateJob {
public void updateActiveShipments() {
List<Shipment> active = shipmentRepository.findByStatus(IN_TRANSIT);
for (Shipment shipment : active) {
try {
updateShipmentTracking(shipment);
} catch (Exception e) {
// Log and continue
}
}
}
private void updateShipmentTracking(Shipment shipment) {
ICarrierAdapter adapter = adapterRegistry.getAdapter(shipment.getCarrier());
TrackingInfo info = adapter.getTracking(shipment.getTrackingNumber());
// Update shipment with new tracking events
shipment.updateTracking(info);
shipmentRepository.save(shipment);
}
}
Comprehensive management of shipments from creation through final delivery.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
{
"specversion": "1.0",
"type": "com.paklog.shipment.created",
"source": "shipment-transportation-service",
"id": "evt-shipment-123",
"time": "2025-10-18T10:30:00Z",
"data": {
"shipmentId": "SHIP-12345",
"orderId": "ORD-67890",
"carrier": "FEDEX",
"serviceLevel": "GROUND",
"trackingNumber": "123456789012",
"estimatedDelivery": "2025-10-22T17:00:00Z",
"packages": [
{
"packageId": "PKG-001",
"trackingNumber": "123456789012",
"weight": {"value": 5.5, "unit": "POUNDS"}
}
]
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"specversion": "1.0",
"type": "com.paklog.shipment.delivered",
"source": "shipment-transportation-service",
"id": "evt-delivery-456",
"time": "2025-10-22T15:30:00Z",
"data": {
"shipmentId": "SHIP-12345",
"orderId": "ORD-67890",
"trackingNumber": "123456789012",
"deliveredAt": "2025-10-22T15:30:00Z",
"recipientName": "John Doe",
"deliveryLocation": "Front door",
"signature": true
}
}
The Shipment & Transportation Service provides comprehensive shipment management:
Business Impact: 10-20% cost savings through carrier optimization, >95% on-time delivery, hourly tracking updates, <2 second label generation.