Skip to content

Online Ordering Context

The Online Ordering context manages the online ordering experience including menu browsing, cart management, fulfillment selection (pickup or delivery), and checkout. It is deliberately decoupled from the Delivery context so electronic menus can be developed independently.

Purpose

Online customers interact with the website or mobile app to browse the menu, add items to a cart, choose pickup or delivery, and pay. This context handles everything up to and including payment. If the customer chose delivery, the order is handed off to the Delivery context via an adaptor.

Interview Connection

From the Online Customer's interview:

"Their website and app aren't always working. Or if it is working it can be really slow."

"When it doesn't work at all, I usually just don't bother. When that happens I will usually just order from the other place down the street."

Isolating online ordering in its own context means it can be deployed, scaled, and maintained independently from the restaurant's dine-in systems. A kitchen outage doesn't crash the online menu.

Types

type OnlineOrderId is Id(OnlineOrdering.OnlineOrder) with {
  briefly "Online order identifier"
  described by "Unique identifier for an online order."
}

type CustomerId is UUID with {
  briefly "Customer identifier"
  described by "Unique identifier for the online customer."
}

type FulfillmentType is any of {
  PickupFulfillment,
  DeliveryFulfillment
} with {
  briefly "Fulfillment type"
  described by "Whether the order is for pickup or delivery."
}

type OnlineOrderStatus is any of {
  Browsing,
  CartReady,
  FulfillmentChosen,
  OnlineSubmitted,
  OnlinePaid,
  OnlineInPreparation,
  ReadyForPickup,
  OutForDelivery,
  OnlineCompleted
} with {
  briefly "Online order status"
  described by "Current status of an online order."
}

type CartItem is {
  cartMenuItemId is String(1, 50)
  cartItemName is String(1, 200)
  cartItemPrice is Decimal(8, 2)
  cartItemQuantity is Natural
  cartItemNotes is optional String(1, 500)
} with {
  briefly "Cart item"
  described by "An item in the online shopping cart."
}

type DeliveryAddress is {
  streetAddress is String(1, 200)
  deliveryCity is String(1, 100)
  deliveryState is String(2, 2)
  deliveryZipCode is String(5, 10)
  deliveryInstructions is optional String(1, 500)
} with {
  briefly "Delivery address"
  described by "Address for delivery fulfillment."
}

Note the 9-value OnlineOrderStatus enumeration — online orders have more states than dine-in orders because they include fulfillment tracking.

Entity: OnlineOrder

The OnlineOrder entity has a 6-command lifecycle:

entity OnlineOrder is {

  command BrowseMenu is {
    onlineOrderId is OnlineOrderId
    customerId is CustomerId
    browsedAt is TimeStamp
  }

  command AddToCart is {
    onlineOrderId is OnlineOrderId
    cartItem is CartItem
  }

  command RemoveFromCart is {
    onlineOrderId is OnlineOrderId
    removedCartItemId is String(1, 50)
  }

  command SelectFulfillment is {
    onlineOrderId is OnlineOrderId
    fulfillmentType is FulfillmentType
    deliveryAddress is optional DeliveryAddress
    requestedTime is optional TimeStamp
  }

  command SubmitOnlineOrder is {
    onlineOrderId is OnlineOrderId
    onlineSubmittedAt is TimeStamp
  }

  command ProcessOnlinePayment is {
    onlineOrderId is OnlineOrderId
    onlinePayment is OnlinePaymentInfo
  }

  // Events: MenuBrowsed, ItemAddedToCart, ItemRemovedFromCart,
  //         FulfillmentSelected, OnlineOrderSubmitted,
  //         OnlinePaymentProcessed

  state ActiveOnlineOrder of OnlineOrder.OnlineOrderStateData

  handler OnlineOrderHandler is {
    on command BrowseMenu {
      morph entity OnlineOrdering.OnlineOrder to state
        OnlineOrdering.OnlineOrder.ActiveOnlineOrder
        with command BrowseMenu
      tell event MenuBrowsed to
        entity OnlineOrdering.OnlineOrder
    }
    on command AddToCart {
      tell event ItemAddedToCart to
        entity OnlineOrdering.OnlineOrder
    }
    on command RemoveFromCart {
      tell event ItemRemovedFromCart to
        entity OnlineOrdering.OnlineOrder
    }
    on command SelectFulfillment {
      tell event FulfillmentSelected to
        entity OnlineOrdering.OnlineOrder
    }
    on command SubmitOnlineOrder {
      tell event OnlineOrderSubmitted to
        entity OnlineOrdering.OnlineOrder
    }
    on command ProcessOnlinePayment {
      tell event OnlinePaymentProcessed to
        entity OnlineOrdering.OnlineOrder
    }
  }
}

Note that SelectFulfillment uses optional DeliveryAddress — the address is only required for delivery fulfillment, not pickup. The optional keyword in RIDDL makes this explicit in the model.

Repository

repository OnlineOrderRepository is {
  schema OnlineOrderData is relational
    of onlineOrders as OnlineOrder
    index on field OnlineOrder.onlineOrderId
    index on field OnlineOrder.customerId
    index on field OnlineOrder.onlineOrderStatus
}

The index on customerId supports order history lookups — the online customer mentioned "they have all of that stuff on file already."

Adaptors

Online Ordering has three outbound adaptors:

adaptor ToKitchen to context Restaurant.Kitchen is {
  handler OnlineKitchenRouting is {
    on command Restaurant.Kitchen.KitchenTicket.ReceiveTicket {
      prompt "Convert submitted online order into kitchen ticket"
    }
  }
}

adaptor ToDelivery to context Restaurant.Delivery is {
  handler DeliveryRouting is {
    on command Restaurant.Delivery.DeliveryOrder.CreateDelivery {
      prompt "Create delivery when delivery fulfillment is selected"
    }
  }
} with {
  briefly "Delivery adaptor"
  described by {
    | Sends orders with delivery fulfillment to the
    | delivery context. This decoupling enables electronic
    | menus to operate independently of delivery.
  }
}

adaptor ToLoyalty to context Restaurant.Loyalty is {
  handler OnlineLoyaltyRouting is {
    on command Restaurant.Loyalty.LoyaltyAccount.AccruePoints {
      prompt "Send online payment event to loyalty for point accrual"
    }
  }
}

Design Decisions

Why decouple from Delivery? The CEO wanted electronic menus. The Head Chef wanted streamlined menu distribution. If online ordering and delivery were a single context, you couldn't ship the electronic menu without also completing the delivery rewrite. Decoupling means the menu experience can launch first, with delivery improvements following independently.

Why BrowseMenu creates the entity? The morph on BrowseMenu creates the online order session. This captures the browsing-to-purchase funnel in the event stream, enabling analytics on cart abandonment and conversion rates.

Why separate from FrontOfHouse? Online and dine-in orders have different types (CartItem vs OrderLine), different flows (cart + fulfillment selection vs table + server), and different scalability requirements. A busy Friday night at the restaurant shouldn't slow down the website.

Source