1// Package cargo contains the heart of the domain model. 2package cargo 3 4import ( 5 "errors" 6 "strings" 7 "time" 8 9 "github.com/pborman/uuid" 10 11 "github.com/go-kit/kit/examples/shipping/location" 12) 13 14// TrackingID uniquely identifies a particular cargo. 15type TrackingID string 16 17// Cargo is the central class in the domain model. 18type Cargo struct { 19 TrackingID TrackingID 20 Origin location.UNLocode 21 RouteSpecification RouteSpecification 22 Itinerary Itinerary 23 Delivery Delivery 24} 25 26// SpecifyNewRoute specifies a new route for this cargo. 27func (c *Cargo) SpecifyNewRoute(rs RouteSpecification) { 28 c.RouteSpecification = rs 29 c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary) 30} 31 32// AssignToRoute attaches a new itinerary to this cargo. 33func (c *Cargo) AssignToRoute(itinerary Itinerary) { 34 c.Itinerary = itinerary 35 c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary) 36} 37 38// DeriveDeliveryProgress updates all aspects of the cargo aggregate status 39// based on the current route specification, itinerary and handling of the cargo. 40func (c *Cargo) DeriveDeliveryProgress(history HandlingHistory) { 41 c.Delivery = DeriveDeliveryFrom(c.RouteSpecification, c.Itinerary, history) 42} 43 44// New creates a new, unrouted cargo. 45func New(id TrackingID, rs RouteSpecification) *Cargo { 46 itinerary := Itinerary{} 47 history := HandlingHistory{make([]HandlingEvent, 0)} 48 49 return &Cargo{ 50 TrackingID: id, 51 Origin: rs.Origin, 52 RouteSpecification: rs, 53 Delivery: DeriveDeliveryFrom(rs, itinerary, history), 54 } 55} 56 57// Repository provides access a cargo store. 58type Repository interface { 59 Store(cargo *Cargo) error 60 Find(id TrackingID) (*Cargo, error) 61 FindAll() []*Cargo 62} 63 64// ErrUnknown is used when a cargo could not be found. 65var ErrUnknown = errors.New("unknown cargo") 66 67// NextTrackingID generates a new tracking ID. 68// TODO: Move to infrastructure(?) 69func NextTrackingID() TrackingID { 70 return TrackingID(strings.Split(strings.ToUpper(uuid.New()), "-")[0]) 71} 72 73// RouteSpecification Contains information about a route: its origin, 74// destination and arrival deadline. 75type RouteSpecification struct { 76 Origin location.UNLocode 77 Destination location.UNLocode 78 ArrivalDeadline time.Time 79} 80 81// IsSatisfiedBy checks whether provided itinerary satisfies this 82// specification. 83func (s RouteSpecification) IsSatisfiedBy(itinerary Itinerary) bool { 84 return itinerary.Legs != nil && 85 s.Origin == itinerary.InitialDepartureLocation() && 86 s.Destination == itinerary.FinalArrivalLocation() 87} 88 89// RoutingStatus describes status of cargo routing. 90type RoutingStatus int 91 92// Valid routing statuses. 93const ( 94 NotRouted RoutingStatus = iota 95 Misrouted 96 Routed 97) 98 99func (s RoutingStatus) String() string { 100 switch s { 101 case NotRouted: 102 return "Not routed" 103 case Misrouted: 104 return "Misrouted" 105 case Routed: 106 return "Routed" 107 } 108 return "" 109} 110 111// TransportStatus describes status of cargo transportation. 112type TransportStatus int 113 114// Valid transport statuses. 115const ( 116 NotReceived TransportStatus = iota 117 InPort 118 OnboardCarrier 119 Claimed 120 Unknown 121) 122 123func (s TransportStatus) String() string { 124 switch s { 125 case NotReceived: 126 return "Not received" 127 case InPort: 128 return "In port" 129 case OnboardCarrier: 130 return "Onboard carrier" 131 case Claimed: 132 return "Claimed" 133 case Unknown: 134 return "Unknown" 135 } 136 return "" 137} 138