Menu Management Context¶
The Menu Management context manages the corporate menu lifecycle
including recipe development, pricing, and atomic menu
distribution to all 500+ locations via the MenuRelease entity.
It addresses the Head Chef's monthly coordination bottleneck by
enabling versioned, atomic menu updates.
Purpose¶
Every month, the Head Chef develops new recipes, adjusts pricing, and needs to distribute the updated menu simultaneously to all locations, the website, the printing service, and the photography service. Previously, this required manual coordination across multiple teams and systems.
Interview Connection¶
From the Head Chef's interview:
"The menu is decided on monthly and distributed via email."
"The biggest headache is coordinating the monthly menu update across all locations, the website, the printers..."
The MenuRelease entity models this as an atomic operation:
changes are bundled, reviewed, and published simultaneously.
Types¶
type MenuItemId is Id(MenuManagement.MenuItem) with {
briefly "Menu item identifier"
described by "Unique identifier for a corporate menu item."
}
type MenuReleaseId is Id(MenuManagement.MenuRelease) with {
briefly "Menu release identifier"
described by "Unique identifier for a menu release."
}
type MenuItemCategory is any of {
Appetizer,
Entree,
Side,
Dessert,
Beverage,
Special
} with {
briefly "Menu item category"
described by "Category of the menu item."
}
type MenuItemLifecycle is any of {
Draft,
Active,
Seasonal,
Retired
} with {
briefly "Menu item lifecycle"
described by "Current lifecycle stage of a menu item."
}
type MenuReleaseStatus is any of {
ReleaseDraftStatus,
ReleaseFinalizedStatus,
ReleasePublishedStatus,
ReleaseRolledBackStatus
} with {
briefly "Menu release status"
described by "Current status of a menu release."
}
type RecipeInfo is {
recipeInstructions is String(1, 5000)
recipeIngredients is many String(1, 200)
prepTime is Duration
cookTime is Duration
servings is Natural
} with {
briefly "Recipe information"
described by "Recipe details for a menu item."
}
type PriceInfo is {
basePrice is Decimal(8, 2)
costToMake is Decimal(8, 2)
marginPercent is Decimal(5, 2)
} with {
briefly "Price information"
described by "Pricing and cost details for a menu item."
}
Note the RecipeInfo record uses Duration for prep and cook
times — a RIDDL predefined type — and many String for the
ingredients list.
Entity: MenuItem¶
The MenuItem entity manages individual menu items:
entity MenuItem is {
command CreateMenuItem is {
menuItemId is MenuItemId
menuItemName is String(1, 200)
menuItemCategory is MenuItemCategory
menuItemDescription is String(1, 1000)
recipe is RecipeInfo
pricing is PriceInfo
}
command UpdateMenuItem is {
menuItemId is MenuItemId
updatedName is optional String(1, 200)
updatedDescription is optional String(1, 1000)
updatedRecipe is optional RecipeInfo
}
command SetPrice is {
menuItemId is MenuItemId
updatedPricing is PriceInfo
effectiveDate is Date
}
command RetireMenuItem is {
menuItemId is MenuItemId
retiredReason is String(1, 500)
}
// Events: MenuItemCreated, MenuItemUpdated, PriceSet,
// MenuItemRetired
state ActiveMenuItem of MenuItem.MenuItemStateData
handler MenuItemHandler is {
on command CreateMenuItem {
morph entity MenuManagement.MenuItem to state
MenuManagement.MenuItem.ActiveMenuItem
with command CreateMenuItem
tell event MenuItemCreated to
entity MenuManagement.MenuItem
}
on command UpdateMenuItem {
tell event MenuItemUpdated to
entity MenuManagement.MenuItem
}
on command SetPrice {
tell event PriceSet to
entity MenuManagement.MenuItem
}
on command RetireMenuItem {
tell event MenuItemRetired to
entity MenuManagement.MenuItem
}
}
}
Note that UpdateMenuItem uses optional fields — you can
update the name, description, or recipe independently without
providing all fields every time.
Entity: MenuRelease¶
The MenuRelease entity is the key innovation — it bundles
menu changes into an atomic release:
entity MenuRelease is {
command CreateMenuRelease is {
menuReleaseId is MenuReleaseId
releaseName is String(1, 200)
releaseDescription is String(1, 1000)
effectiveDate is Date
}
command AddItemToRelease is {
menuReleaseId is MenuReleaseId
releaseMenuItemId is MenuItemId
releaseAction is String(1, 50)
}
command FinalizeRelease is {
menuReleaseId is MenuReleaseId
finalizedAt is TimeStamp
}
command PublishRelease is {
menuReleaseId is MenuReleaseId
publishedAt is TimeStamp
}
command RollbackRelease is {
menuReleaseId is MenuReleaseId
rollbackReason is String(1, 500)
rolledBackAt is TimeStamp
}
// Events: MenuReleaseCreated, ItemAddedToRelease,
// ReleaseFinalized, ReleasePublished, ReleaseRolledBack
state ActiveRelease of MenuRelease.MenuReleaseStateData
handler MenuReleaseHandler is {
on command CreateMenuRelease {
morph entity MenuManagement.MenuRelease to state
MenuManagement.MenuRelease.ActiveRelease
with command CreateMenuRelease
tell event MenuReleaseCreated to
entity MenuManagement.MenuRelease
}
// ... remaining commands follow tell pattern
}
}
The lifecycle: Create → Add Items → Finalize → Publish →
(optional) Rollback. The releaseAction field on
AddItemToRelease supports different actions per item: add,
update, remove, or price-change.
The ReleasePublished event is the key moment:
event ReleasePublished is {
menuReleaseId is MenuReleaseId
releaseName is String(1, 200)
effectiveDate is Date
publishedAt is TimeStamp
} with {
briefly "Release published"
described by {
| Emitted when a menu release is published to all
| locations. This is the atomic distribution event
| that updates menus chain-wide simultaneously.
}
}
Repositories¶
repository MenuItemRepository is {
schema MenuItemData is relational
of menuItems as MenuItem
index on field MenuItem.menuItemId
index on field MenuItem.menuItemCategory
index on field MenuItem.menuItemLifecycle
}
repository MenuReleaseRepository is {
schema MenuReleaseData is relational
of releases as MenuRelease
index on field MenuRelease.menuReleaseId
index on field MenuRelease.menuReleaseStatus
}
Adaptor¶
Menu Management has an outbound adaptor for distributing menus:
adaptor ToRestaurants to context Restaurant.FrontOfHouse is {
handler MenuDistribution is {
on command Restaurant.FrontOfHouse.TableOrder.CreateOrder {
prompt "Distribute published menu updates to restaurant locations"
}
}
}
Design Decisions¶
Why atomic releases instead of individual item updates?
Updating menu items one at a time risks inconsistency — some
locations might have the new price while others still show the
old one. The MenuRelease entity bundles all changes and
publishes them atomically, ensuring all 500+ locations update
simultaneously.
Why separate MenuItem and MenuRelease entities? MenuItems represent the master catalog and evolve over time. MenuReleases are point-in-time snapshots of changes to be distributed. An item might be updated many times before being included in a release.
Solving the coordination bottleneck: The Head Chef
described a manual process involving email coordination with
printers, web teams, and locations. The MenuRelease entity
replaces this with a structured workflow: draft → add items →
finalize (review gate) → publish (atomic distribution) → optional
rollback.