Skip to main content
added 8 characters in body
Source Link
namespace OrderDispatcher

open Shared
open Language

module Operations =

    type AvailableTruckers      = AvailableTruckersRequest         -> AsyncResult<unitAsyncResult<TruckersOpen,Error>
    type DispatchTrucker        = DispatchTruckerSubmission        -> AsyncResult<unit,Error>
    type CancelledOrder         = OrderCancellationSubmission      -> AsyncResult<OrderCancellationReceipt,Error>
    type ChangeOrderAcquisition = OrderAcquisitionChangeSubmission -> AsyncResult<UnstartedOrder,Error>
namespace OrderDispatcher

open Shared
open Language

module Operations =

    type AvailableTruckers      = AvailableTruckersRequest         -> AsyncResult<unit,Error>
    type DispatchTrucker        = DispatchTruckerSubmission        -> AsyncResult<unit,Error>
    type CancelledOrder         = OrderCancellationSubmission      -> AsyncResult<OrderCancellationReceipt,Error>
    type ChangeOrderAcquisition = OrderAcquisitionChangeSubmission -> AsyncResult<UnstartedOrder,Error>
namespace OrderDispatcher

open Shared
open Language

module Operations =

    type AvailableTruckers      = AvailableTruckersRequest         -> AsyncResult<TruckersOpen,Error>
    type DispatchTrucker        = DispatchTruckerSubmission        -> AsyncResult<unit,Error>
    type CancelledOrder         = OrderCancellationSubmission      -> AsyncResult<OrderCancellationReceipt,Error>
    type ChangeOrderAcquisition = OrderAcquisitionChangeSubmission -> AsyncResult<UnstartedOrder,Error>
deleted 67 characters in body
Source Link
namespace Trucker

open Shared
open Language

module Common =

    type QueryUnstartedOrders = AuthenticatedTrucker -> AsyncResult<UnstartedOrders,Error>

module NewOrderPending =

    type AcceptOrderRequest   = OrderResponseSubmission -> AsyncResult<unit,Error>
    type DeclineOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>
    type ForfeitOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>

module AcceptedOrder =

    type CancelAcceptance  = CancellableOrder         -> AsyncResult<unit,Error>
    type StartInTransit    = OrderProduced            -> AsyncResult<InTransitToPickupTrucker,Error>
    type InTransitToPickup = InTransitToPickupTrucker -> AsyncResult<IntransitToPickupOrder  ,Error>

    //----------------------------------------------------------------------------------------
    // Handle change of how order is acquired (i.e. pickup or delivery)
    //----------------------------------------------------------------------------------------
    type MyDelegate = delegate of obj * OrderCancelled -> unit
    
    type IOrderCancelled =
        [<CLIEvent>]
        abstract member OrderCancelled : IEvent<MyDelegate, OrderCancelled>
    
    type IncomingNotification () =
        let orderCancelled = new Event<MyDelegate, OrderCancelled> ()
    
        interface IOrderCancelled with
            [<CLIEvent>]
            member x.OrderCancelled = orderCancelled.Publish
    //----------------------------------------------------------------------------------------

module InTransitToDropoff =

    type CancelAcceptance   = InTransitToDropoffTrucker -> AsyncResult<OrderCancellationReceipt,Error>
    type InTransitToDropoff = InTransitToDropoffTrucker -> AsyncResult<IntransitToDropoffOrder ,Error>
    type ClaimDelivered     = InTransitToDropoffTrucker -> AsyncResult<OrderClosed          ,Error>

module OrdersCompleted =

    type CloseTruck = CloseTruckSubmission -> AsyncResult<ClosedTruckReceipt,Error>
module Shared

type AsyncResult<'T,'error> = Async<Result<'T,'error>>
type Error = string
 
type OrderId    
type OrderId         = string
type TruckerId            = string
type CustomerId           = string
type ItemId               = string
type Name                 = string
type Description          = string
type Response             = string
type Address              = string
type Weight               = float
type feet                 = float
type AcquisitionType      = string
type CancellationId       = string
type OrderStatus          = string
 
type Dimensions = {
    Length : feet
    Width  : feet
    Height : feet
}

type AuthenticatedTrucker = {
    TruckerId : TruckerId
}

type OpenedTrucker = {
    Trucker : AuthenticatedTrucker
}

type Item = {
    ItemId      : ItemId
    Name        : Name
    Description : Description
    Weight      : Weight
    Dimensions  : Dimensions
}

type ItemQty = {
    Item : Item
    Qty  : int
}

type ItemQtys = ItemQty seq

type Pickup = {
    Address  : Address
    ItemQtys : ItemQtys
}

type Customer = {
    CustomerId : CustomerId
    Address    : Address
}

type Order = {
    OrderId    : OrderId
    Customer   : Customer
    Pickup     : Pickup
    Status     : OrderStatus
}

type OrderProduced = {
    Order : Order
}

type OrderInTransit = {
    OrderProduced : OrderProduced
}

type OrderClosed = {
    OrderInTransit : OrderInTransit
}

type OrderCancelled = {
    Order : Order
}

type OrderCancellationSubmission = {
    Order  : Order
    Reason : string
}

type OrderCancellationReceipt = {
    CancellationId : CancellationId
    Order          : Order
    Reason         : string
}

type OrderAcquisitionChangeSubmission = {
    Order           : OrderCancellationReceipt
    AcquisitionType : AcquisitionType
}

type UnstartedOrder = { Order: Order }

type UnstartedOrders = UnstartedOrder seq
namespace Trucker

open Shared
open Language

module Common =

    type QueryUnstartedOrders = AuthenticatedTrucker -> AsyncResult<UnstartedOrders,Error>

module NewOrderPending =

    type AcceptOrderRequest   = OrderResponseSubmission -> AsyncResult<unit,Error>
    type DeclineOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>
    type ForfeitOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>

module AcceptedOrder =

    type CancelAcceptance  = CancellableOrder         -> AsyncResult<unit,Error>
    type StartInTransit    = OrderProduced            -> AsyncResult<InTransitToPickupTrucker,Error>
    type InTransitToPickup = InTransitToPickupTrucker -> AsyncResult<IntransitToPickupOrder  ,Error>

    //----------------------------------------------------------------------------------------
    // Handle change of how order is acquired (i.e. pickup or delivery)
    //----------------------------------------------------------------------------------------
    type MyDelegate = delegate of obj * OrderCancelled -> unit
    
    type IOrderCancelled =
        [<CLIEvent>]
        abstract member OrderCancelled : IEvent<MyDelegate, OrderCancelled>
    
    type IncomingNotification () =
        let orderCancelled = new Event<MyDelegate, OrderCancelled> ()
    
        interface IOrderCancelled with
            [<CLIEvent>]
            member x.OrderCancelled = orderCancelled.Publish
    //----------------------------------------------------------------------------------------

module InTransitToDropoff =

    type CancelAcceptance   = InTransitToDropoffTrucker -> AsyncResult<OrderCancellationReceipt,Error>
    type InTransitToDropoff = InTransitToDropoffTrucker -> AsyncResult<IntransitToDropoffOrder ,Error>
    type ClaimDelivered     = InTransitToDropoffTrucker -> AsyncResult<OrderClosed          ,Error>

module OrdersCompleted =

    type CloseTruck = CloseTruckSubmission -> AsyncResult<ClosedTruckReceipt,Error>
module Shared

type AsyncResult<'T,'error> = Async<Result<'T,'error>>
type Error = string
 
type OrderId              = string
type TruckerId            = string
type CustomerId           = string
type ItemId               = string
type Name                 = string
type Description          = string
type Response             = string
type Address              = string
type Weight               = float
type feet                 = float
type AcquisitionType      = string
type CancellationId       = string
type OrderStatus          = string
 
type Dimensions = {
    Length : feet
    Width  : feet
    Height : feet
}

type AuthenticatedTrucker = {
    TruckerId : TruckerId
}

type OpenedTrucker = {
    Trucker : AuthenticatedTrucker
}

type Item = {
    ItemId      : ItemId
    Name        : Name
    Description : Description
    Weight      : Weight
    Dimensions  : Dimensions
}

type ItemQty = {
    Item : Item
    Qty  : int
}

type ItemQtys = ItemQty seq

type Pickup = {
    Address  : Address
    ItemQtys : ItemQtys
}

type Customer = {
    CustomerId : CustomerId
    Address    : Address
}

type Order = {
    OrderId    : OrderId
    Customer   : Customer
    Pickup     : Pickup
    Status     : OrderStatus
}

type OrderProduced = {
    Order : Order
}

type OrderInTransit = {
    OrderProduced : OrderProduced
}

type OrderClosed = {
    OrderInTransit : OrderInTransit
}

type OrderCancelled = {
    Order : Order
}

type OrderCancellationSubmission = {
    Order  : Order
    Reason : string
}

type OrderCancellationReceipt = {
    CancellationId : CancellationId
    Order          : Order
    Reason         : string
}

type OrderAcquisitionChangeSubmission = {
    Order           : OrderCancellationReceipt
    AcquisitionType : AcquisitionType
}

type UnstartedOrder = { Order: Order }

type UnstartedOrders = UnstartedOrder seq
namespace Trucker

open Shared
open Language

module Common =

    type QueryUnstartedOrders = AuthenticatedTrucker -> AsyncResult<UnstartedOrders,Error>

module NewOrderPending =

    type AcceptOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>
    type DeclineOrderRequest = OrderResponseSubmission -> AsyncResult<unit,Error>
    type ForfeitOrderRequest = OrderResponseSubmission -> AsyncResult<unit,Error>

module AcceptedOrder =

    type CancelAcceptance  = CancellableOrder         -> AsyncResult<unit,Error>
    type StartInTransit    = OrderProduced            -> AsyncResult<InTransitToPickupTrucker,Error>
    type InTransitToPickup = InTransitToPickupTrucker -> AsyncResult<IntransitToPickupOrder  ,Error>

    //----------------------------------------------------------------------------------------
    // Handle change of how order is acquired (i.e. pickup or delivery)
    //----------------------------------------------------------------------------------------
    type MyDelegate = delegate of obj * OrderCancelled -> unit
    
    type IOrderCancelled =
        [<CLIEvent>]
        abstract member OrderCancelled : IEvent<MyDelegate, OrderCancelled>
    
    type IncomingNotification () =
        let orderCancelled = new Event<MyDelegate, OrderCancelled> ()
    
        interface IOrderCancelled with
            [<CLIEvent>]
            member x.OrderCancelled = orderCancelled.Publish
    //----------------------------------------------------------------------------------------

module InTransitToDropoff =

    type CancelAcceptance   = InTransitToDropoffTrucker -> AsyncResult<OrderCancellationReceipt,Error>
    type InTransitToDropoff = InTransitToDropoffTrucker -> AsyncResult<IntransitToDropoffOrder ,Error>
    type ClaimDelivered     = InTransitToDropoffTrucker -> AsyncResult<OrderClosed          ,Error>

module OrdersCompleted =

    type CloseTruck = CloseTruckSubmission -> AsyncResult<ClosedTruckReceipt,Error>
module Shared

type AsyncResult<'T,'error> = Async<Result<'T,'error>>
type Error = string
    
type OrderId         = string
type TruckerId       = string
type CustomerId      = string
type ItemId          = string
type Name            = string
type Description     = string
type Response        = string
type Address         = string
type Weight          = float
type feet            = float
type AcquisitionType = string
type CancellationId  = string
type OrderStatus     = string
type Dimensions = {
    Length : feet
    Width  : feet
    Height : feet
}

type AuthenticatedTrucker = {
    TruckerId : TruckerId
}

type OpenedTrucker = {
    Trucker : AuthenticatedTrucker
}

type Item = {
    ItemId      : ItemId
    Name        : Name
    Description : Description
    Weight      : Weight
    Dimensions  : Dimensions
}

type ItemQty = {
    Item : Item
    Qty  : int
}

type ItemQtys = ItemQty seq

type Pickup = {
    Address  : Address
    ItemQtys : ItemQtys
}

type Customer = {
    CustomerId : CustomerId
    Address    : Address
}

type Order = {
    OrderId    : OrderId
    Customer   : Customer
    Pickup     : Pickup
    Status     : OrderStatus
}

type OrderProduced = {
    Order : Order
}

type OrderInTransit = {
    OrderProduced : OrderProduced
}

type OrderClosed = {
    OrderInTransit : OrderInTransit
}

type OrderCancelled = {
    Order : Order
}

type OrderCancellationSubmission = {
    Order  : Order
    Reason : string
}

type OrderCancellationReceipt = {
    CancellationId : CancellationId
    Order          : Order
    Reason         : string
}

type OrderAcquisitionChangeSubmission = {
    Order           : OrderCancellationReceipt
    AcquisitionType : AcquisitionType
}

type UnstartedOrder = { Order: Order }

type UnstartedOrders = UnstartedOrder seq
Source Link

enter image description here

Here's how I would domain model this... I usually decompose business requirements into core operations. Once I have identified those core operations, I then identify the sub-domains that would host those operations.

Note, I prefer to identify operations first because it's easier for me to identify what types are required in order for those operations to succeed.

Note:

Just because OOP languages such as C# or Java are general purpose doesn't mean that they are ideal languages for modeling business domains. Thus, I sincerely believe that statically typed FP languages are a natural fit for modeling business domains due to less syntax along with higher information density.

In regards to data persistence, I don't think a relational database is ideal. I would use an event store (i.e. immutable database) so that data cannot be overwritten or deleted. After all, this domain is about operating on historical domain events that should never be updated or deleted (only appended).

I have provided the following model given the description of the domain:

CustomerOrder.Operations

namespace CustomerOrder

open Shared
open Language

module Operations =

    type PlaceOrder        = PlaceOrderSubmission        -> AsyncResult<unit,Error>
    type ChangeAcquisition = ChangeAcquisitionSubmission -> AsyncResult<unit,Error>

CustomerOrder.Language

module Language

open Shared

type AuthenticatedCustomer = TODO

type AcquisitionType = string // Ex: CustomerPickup | TruckDelivery

type PlaceOrderSubmission = {

    AuthenticatedCustomer : AuthenticatedCustomer
    Order                 : Order
    OrderRequestType      : AcquisitionType
}

type ChangeAcquisitionSubmission = {

    OrderSubmission       : PlaceOrderSubmission
    NewAcquisitionRequest : AcquisitionType
}

OrderDispatcher.Operations

namespace OrderDispatcher

open Shared
open Language

module Operations =

    type AvailableTruckers      = AvailableTruckersRequest         -> AsyncResult<unit,Error>
    type DispatchTrucker        = DispatchTruckerSubmission        -> AsyncResult<unit,Error>
    type CancelledOrder         = OrderCancellationSubmission      -> AsyncResult<OrderCancellationReceipt,Error>
    type ChangeOrderAcquisition = OrderAcquisitionChangeSubmission -> AsyncResult<UnstartedOrder,Error>

OrderDispatcher.Language

module Language

open Shared

type TruckerId = string

type Trucker = {
    TruckerId : TruckerId
}

type DispatchTruckerSubmission = {
    Trucker : Trucker
    Order   : Order
}

type AvailableTruckersRequest = {
    Order   : Order
}

Trucker.Operations

namespace Trucker

open Shared
open Language

module Common =

    type QueryUnstartedOrders = AuthenticatedTrucker -> AsyncResult<UnstartedOrders,Error>

module NewOrderPending =

    type AcceptOrderRequest   = OrderResponseSubmission -> AsyncResult<unit,Error>
    type DeclineOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>
    type ForfeitOrderRequest  = OrderResponseSubmission -> AsyncResult<unit,Error>

module AcceptedOrder =

    type CancelAcceptance  = CancellableOrder         -> AsyncResult<unit,Error>
    type StartInTransit    = OrderProduced            -> AsyncResult<InTransitToPickupTrucker,Error>
    type InTransitToPickup = InTransitToPickupTrucker -> AsyncResult<IntransitToPickupOrder  ,Error>

    //----------------------------------------------------------------------------------------
    // Handle change of how order is acquired (i.e. pickup or delivery)
    //----------------------------------------------------------------------------------------
    type MyDelegate = delegate of obj * OrderCancelled -> unit
    
    type IOrderCancelled =
        [<CLIEvent>]
        abstract member OrderCancelled : IEvent<MyDelegate, OrderCancelled>
    
    type IncomingNotification () =
        let orderCancelled = new Event<MyDelegate, OrderCancelled> ()
    
        interface IOrderCancelled with
            [<CLIEvent>]
            member x.OrderCancelled = orderCancelled.Publish
    //----------------------------------------------------------------------------------------

module InTransitToDropoff =

    type CancelAcceptance   = InTransitToDropoffTrucker -> AsyncResult<OrderCancellationReceipt,Error>
    type InTransitToDropoff = InTransitToDropoffTrucker -> AsyncResult<IntransitToDropoffOrder ,Error>
    type ClaimDelivered     = InTransitToDropoffTrucker -> AsyncResult<OrderClosed          ,Error>

module OrdersCompleted =

    type CloseTruck = CloseTruckSubmission -> AsyncResult<ClosedTruckReceipt,Error>

Trucker.Language

module rec Language

open Shared

type TruckerStatus =
    | Open      of OpenedTrucker
    | InTransit of InTransitTrucker
    | Completed of CompletedTrucker

type AcceptedOrder = { 
    Trucker : OpenedTrucker
}

type IntransitToPickupOrder = { 
    Trucker : InTransitTrucker
}

type IntransitToDropoffOrder = { 
    Trucker : InTransitTrucker
}

type CompletedOrder = {
    Trucker : CompletedTrucker
}

type OrderResponseSubmission = {
    OpenedTrucker  : OpenedTrucker
    UnstartedOrder : UnstartedOrder
    Response       : Response
}

type InTransitTrucker = {
    Trucker        : AuthenticatedTrucker
    CurrentOrder   : OrderProduced
    OrdersProduced : OrderProduced seq
    OrdersClosed   : OrderProduced seq
}

type InTransitToPickupTrucker = {
    Trucker : AuthenticatedTrucker
    Order   : OrderInTransit
}

type InTransitToDropoffTrucker = {
    Trucker : AuthenticatedTrucker
    Order   : OrderInTransit
}

type CompletedTrucker = {
    Trucker      : AuthenticatedTrucker
    OrdersClosed : OrderProduced seq
}

type ArrivedAtDropoffSubmission = {
    Trucker : InTransitTrucker
}

type CancellableOrder =
    | OpenedTrucker    of OpenedTrucker
    | InTransitTrucker of InTransitTrucker

type CloseTruckSubmission = {
    OrdersClosed : OrderClosed seq
}

type ClosedTruckReceipt = {
    OrdersClosed : OrderClosed seq
}

Shared Language

module Shared

type AsyncResult<'T,'error> = Async<Result<'T,'error>>
type Error = string

type OrderId              = string
type TruckerId            = string
type CustomerId           = string
type ItemId               = string
type Name                 = string
type Description          = string
type Response             = string
type Address              = string
type Weight               = float
type feet                 = float
type AcquisitionType      = string
type CancellationId       = string
type OrderStatus          = string

type Dimensions = {
    Length : feet
    Width  : feet
    Height : feet
}

type AuthenticatedTrucker = {
    TruckerId : TruckerId
}

type OpenedTrucker = {
    Trucker : AuthenticatedTrucker
}

type Item = {
    ItemId      : ItemId
    Name        : Name
    Description : Description
    Weight      : Weight
    Dimensions  : Dimensions
}

type ItemQty = {
    Item : Item
    Qty  : int
}

type ItemQtys = ItemQty seq

type Pickup = {
    Address  : Address
    ItemQtys : ItemQtys
}

type Customer = {
    CustomerId : CustomerId
    Address    : Address
}

type Order = {
    OrderId    : OrderId
    Customer   : Customer
    Pickup     : Pickup
    Status     : OrderStatus
}

type OrderProduced = {
    Order : Order
}

type OrderInTransit = {
    OrderProduced : OrderProduced
}

type OrderClosed = {
    OrderInTransit : OrderInTransit
}

type OrderCancelled = {
    Order : Order
}

type OrderCancellationSubmission = {
    Order  : Order
    Reason : string
}

type OrderCancellationReceipt = {
    CancellationId : CancellationId
    Order          : Order
    Reason         : string
}

type OrderAcquisitionChangeSubmission = {
    Order           : OrderCancellationReceipt
    AcquisitionType : AcquisitionType
}

type UnstartedOrder = { Order: Order }

type UnstartedOrders = UnstartedOrder seq