| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 | // Package shipping contains the heart of the domain model.package modelimport (	"errors"	"strings"	"time"	"github.com/pborman/uuid")// TrackingID uniquely identifies a particular cargo.type TrackingID string// Cargo is the central class in the domain model.type Cargo struct {	TrackingID         TrackingID	Origin             UNLocode	RouteSpecification RouteSpecification	Itinerary          Itinerary	Delivery           Delivery}// SpecifyNewRoute specifies a new route for this cargo.func (c *Cargo) SpecifyNewRoute(rs RouteSpecification) {	c.RouteSpecification = rs	c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary)}// AssignToRoute attaches a new itinerary to this cargo.func (c *Cargo) AssignToRoute(itinerary Itinerary) {	c.Itinerary = itinerary	c.Delivery = c.Delivery.UpdateOnRouting(c.RouteSpecification, c.Itinerary)}// DeriveDeliveryProgress updates all aspects of the cargo aggregate status// based on the current route specification, itinerary and handling of the cargo.func (c *Cargo) DeriveDeliveryProgress(history HandlingHistory) {	c.Delivery = DeriveDeliveryFrom(c.RouteSpecification, c.Itinerary, history)}// NewCargo creates a new, unrouted cargo.func NewCargo(id TrackingID, rs RouteSpecification) *Cargo {	itinerary := Itinerary{}	history := HandlingHistory{make([]HandlingEvent, 0)}	return &Cargo{		TrackingID:         id,		Origin:             rs.Origin,		RouteSpecification: rs,		Delivery:           DeriveDeliveryFrom(rs, itinerary, history),	}}// CargoRepository provides access a cargo store.type CargoRepository interface {	Store(cargo *Cargo) (bool, error)	Find(id TrackingID) (*Cargo, error)	FindAll() []*Cargo}// ErrUnknownCargo is used when a cargo could not be found.var ErrUnknownCargo = errors.New("unknown cargo")// NextTrackingID generates a new tracking ID.// TODO: Move to infrastructure(?)func NextTrackingID() TrackingID {	return TrackingID(strings.Split(strings.ToUpper(uuid.New()), "-")[0])}// RouteSpecification Contains information about a route: its origin,// destination and arrival deadline.type RouteSpecification struct {	Origin          UNLocode	Destination     UNLocode	ArrivalDeadline time.Time}// IsSatisfiedBy checks whether provided itinerary satisfies this// specification.func (s RouteSpecification) IsSatisfiedBy(itinerary Itinerary) bool {	return itinerary.Legs != nil &&		s.Origin == itinerary.InitialDepartureLocation() &&		s.Destination == itinerary.FinalArrivalLocation()}// RoutingStatus describes status of cargo routing.type RoutingStatus int// Valid routing statuses.const (	NotRouted RoutingStatus = iota	Misrouted	Routed)func (s RoutingStatus) String() string {	switch s {	case NotRouted:		return "Not routed"	case Misrouted:		return "Misrouted"	case Routed:		return "Routed"	}	return ""}// TransportStatus describes status of cargo transportation.type TransportStatus int// Valid transport statuses.const (	NotReceived TransportStatus = iota	InPort	OnboardCarrier	Claimed	Unknown)func (s TransportStatus) String() string {	switch s {	case NotReceived:		return "Not received"	case InPort:		return "In port"	case OnboardCarrier:		return "Onboard carrier"	case Claimed:		return "Claimed"	case Unknown:		return "Unknown"	}	return ""}
 |