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