Shipment & Transportation Service - Domain Architecture & Business Capabilities

Service Overview

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


Domain Model & Bounded Context

Bounded Context: Shipment & Transportation

The Shipment & Transportation bounded context is responsible for all aspects of getting packed orders to their destinations via third-party carriers.

Context Boundaries

Responsibilities (What’s IN):

External Dependencies (What’s OUT):

Ubiquitous Language

Core Domain Terms:


Subdomain Classification

Core Domain: Carrier Integration & Shipment Management

Strategic Importance: MEDIUM-HIGH - Enables final delivery to customers

This domain provides critical business value through:

Subdomains:

1. Multi-Carrier Integration (Core)

2. Rate Shopping & Optimization (Core)

3. Label Generation & Documentation (Supporting)

4. Shipment Tracking (Supporting)


Domain Model

Aggregates

1. Shipment (Aggregate Root)

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:


2. Load (Aggregate Root)

Description: 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:


Entities

Package

1
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);
}

TrackingEvent

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
}

Value Objects

Address

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();
}

TrackingInfo

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);
}

RateQuote

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;
}

ProofOfDelivery

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;
}

Domain Services

CarrierSelectionService

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);
}

RateShoppingService

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);
}

DimensionalWeightCalculator

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
}

Carrier Adapter Pattern

ICarrierAdapter Interface

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);
}

FedEx Adapter Implementation

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
}

Application Layer

Ports (Interfaces)

Input Ports (Use Cases)

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);
}

Output Ports (Dependencies)

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);
}

Infrastructure Layer

Adapters

Inbound Adapters

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
    }
}

Background Jobs

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);
    }
}

Business Capabilities

L1: Transportation Management

Comprehensive management of shipments from creation through final delivery.


L2: Shipment Creation & Management

L3.1: Shipment Creation from Packages

L3.2: Multi-Package Shipment Handling

L3.3: Shipment Status Tracking

L3.4: Shipment Cancellation


L2: Carrier Integration & Management

L3.5: FedEx Integration

L3.6: Carrier Adapter Pattern

L3.7: Carrier Credentials Management

L3.8: Carrier Selection Strategy


L2: Rate Shopping & Cost Optimization

L3.9: Multi-Carrier Rate Quotes

L3.10: Dimensional Weight Calculation

L3.11: Surcharge Calculation


L2: Label Generation & Documentation

L3.12: Shipping Label Generation

L3.13: Tracking Number Assignment

L3.14: Customs Documentation (International)


L2: Shipment Tracking & Visibility

L3.15: Real-Time Tracking Updates

L3.16: Tracking Event History

L3.17: Tracking Number Lookup

L3.18: Delivery Confirmation


L2: Load Management & Carrier Tendering

L3.19: Load Creation

L3.20: Load Tendering & Manifesting


L2: Exception Handling

L3.21: Delivery Exception Management


Integration Patterns

Context Mapping

Order Management (Upstream - Customer/Supplier)

Warehouse Operations (Upstream - Partnership)

Product Catalog (Upstream - Conformist)

Carriers (Downstream - Adapter Pattern)


Event Schemas

ShipmentCreatedEvent

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"}
      }
    ]
  }
}

ShipmentDeliveredEvent

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
  }
}

Quality Attributes

Reliability

Performance

Integration


Summary

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.