= yaml = title: Coffee Catalog layout: syncbase toc: false = yaml =
Allows a user to order coffee and related paraphernalia.
type Item struct { Id string // UUID Image BlobRef Desc string Price float } type User struct { Id string // UUID // In reality we might define structs for some of the values below. Name string // full name of user Email string Address string CreditCard string // should instead use a more secure API (token per order?) } type OrderItem struct { ItemId string Count int } type Order struct { Id string // UUID CreatedTime time.Time PlacedTime time.Time // zero value if not yet placed ReceivedTime time.Time // zero value if not yet received // We’d probably have more fields here, e.g. to specify // gift-wrapping options, track order status, etc. }
<app-blessing>-catalog Collection <Item.Id>: Item <user-blessing>-user Collection <User.Id>: User // possibly also include per-user catalog metadata, e.g. favorites <user-blessing>-draftOrders Collection <Order.Id>: Order <Order.Id>/items/ <Item.Id>: OrderItem <user-blessing>-placedOrders Collection - same layout as draftOrders <user-blessing>-processedOrders Collection - same layout as draftOrders
{{# helpers.info }}
We might keep user records in a “users” table and order records in an “orders” table in order to enforce schema, but this is orthogonal to collection layout. {{/ helpers.info }}
When a user “places” an order, all data for the order is moved (copy + delete) atomically from the “draftOrders” to the “placedOrders” collection.
The app cloud backend (order processor) watches for changes to “placedOrders” and processes these orders. After processing an order, it sets Order.ReceivedTime and atomically moves the order record from the “placedOrders” to the “processedOrders” collection. Whenever watch stops for any reason, it is restarted including the initial state to prevent skipping any orders. Order processing must also be either atomic with the move to “processedOrders”, or idempotent to allow safely retrying without executing more than once in case commit fails. It might need to snapshot orders in processing to its own local storage as an extra safeguard (e.g. in case a malicious user changes an order after it’s accepted for processing).
The client app periodically queries “placedOrders” and notifies users of any orders that have not yet been received.
To aid in frontend development, the app would likely implement some ORM-like wrappers and helper functions, e.g. an “order” object that directly contains a list of items (to simplify view rendering) and functions to add, remove, or update items in a draft order (to simplify writing back to the store).
Let “bluebottle” be the blessing for the administrator/owner of the store.
In all cases, both “bluebottle” and user can have syncgroup admin (sgA) permissions to make adding new devices more flexible.