CESSDA Café

The CESSDA Café is inspired by the “12-factor-workshop” by NSD - Norsk Senter for Forskningsdata AS, licensed under CC-BY 3.0. The design of the coffee machine is taken from there (with some modifications to endpoint names).

See the general instructions for what to keep in mind.

Actors

The Customer is not a relevant actor, we will be the Customers, calling via APIs to the Cashier and the Waiter. Customers do not interact with the Coffee Machines.

Process

  1. The Customer places an order with the Cashier.
  2. The Cashier takes the order and returns the orderId to the Customer.
  3. The Cashier starts as many Coffee Machines as needed and available, queuing the rest.
  4. Each Coffee Machine that accepts a job confirms the jobId.
  5. The Customer asks the Waiter for their orderId.
  6. The Waiter requests the order details for the order from the Cashier.
    • The Cashier runs through queued jobs and dispatches them, if possible.
  7. The Cashier returns a list stating which Coffee Machine is brewing which coffee.
  8. The Waiter retrieves the jobs from the Coffee Machines.
  9. The Coffee Machines return the jobs to the Waiter.
  10. The Waiter checks whether it has all coffees for the relevant orderId and returns it on success.

Data-flow diagram

Design

We distinguish between Order and Job, where the Order is used when communicating with the Customer. One Order will map to multiple Jobs that are processed internally.

Products

The following Products are available and must be supported by all Coffee Machines

  • COFFEE
  • STRONG_COFFEE
  • CAPPUCCINO
  • MOCCACHINO
  • COFFEE_WITH_MILK
  • ESPRESSO
  • ESPRESSO_CHOCOLATE
  • KAKAO
  • HOT_WATER

Orders

The order placed by the Customer has the form:

{
  "coffees" : [
    { "product": Product, "count": Integer } ,
    { "product": Product, "count": Integer } ,
    ...
  ]
}

where count is the number of each product ordered and products may be repeated. The sum over all products multiplied by their count gives the orderSize.

Cashier memory

The Cashier maintains a list of Coffee Machines, i.e the urls of the APIs.

The Cashier has to remember the products ordered, but only individual coffees, so it keeps an array of the form:

[
  {
    "product": Product,
    "orderId": UUID,
    "orderPlaced": Timestamp,
    "orderSize": Int,
    "machine": String,
    "jobId": UUID,
    "jobStarted": Timestamp
  }, ...
]

The field job_started defaults to Null until the job is given to a Coffee Machine and then timestamped, i.e. processing has finished for the Cashier. The Cashier can define the jobId and pass it to the Coffee Machine or use the jobId decided by the Coffee Machine.

The Cashier has an API-callable routine that will put the orders to the Coffee Machine on a first-in-first-out basis. This routine is called after each order is placed and whenever its job overviews are called.

Coffee Machine

The Coffee Machine takes some time to produce a coffee, the time is machine-dependent. The Coffee Machine can only produce one coffee at a time, i.e. after the previous one is finished and collected.

It stores the job given by the Cashier in the form

{
  "jobId": UUID,
  "product": Product,
  "jobStarted": Timestamp,
  "jobReady": Timestamp,
  "jobRetrieved": Timestamp
}

The jobId can be given by the Cashier or calculated by the Coffee Machine independently. The jobReady is calculated at order placement, jobRetrieved defaults to Null until collected by the Waiter.

Waiter

The Waiter has an API-callable routine to ask the Cashier for all started jobs and to collect them from the Coffee Machines. This routine is executed first whenever a Customer places an order.

The Waiter stores an array of jobs

[
  {
    "product": Product,
    "orderId": UUID,
    "orderPlaced": Timestamp,
    "orderSize": Int,
    "machine": String,
    "jobStarted": Timestamp,
    "jobRetrieved": Timestamp,
    "orderDelivered": Timestamp
  }, ...
]

The orderDelivered defaults to Null, until the order is picked up by the Customer.

Upon getting the request for an order, the Waiter checks whether it has all items of the order and they are undelivered. If so, the order is delivered by returning

{
  "orderId": UUID,
  "orderPlaced": Timestamp,
  "orderSize": Int,
  "coffees": [
    { "product": Product },
    ....
  ],
  "orderDelivered": Timestamp
}