2

I am trying to design a RESTful (or maybe I should say RESTlike, since I am not designing a fully RESTful API, e.g. not hypertext driven) API as part of a refactoring of an existing "take-a-number" queuing management system.

The basic concepts and workflow are :

  • A given site (location) can propose several different services, and have several desks. Each desk can provide one or more of the services on the site. (e.g. at a bank, you can have an info desk, desks for common operations such as a deposit, and desks where you can sit to speak to a counselor for more complex things, e.g. open a new account or apply for a mortgage)

  • Whe he enters the building, a customer prints a ticket by selecting a service on a touch screen. (e.g. "Open a new account"). From now on the ticket is tracked in the server's internal state.

  • The ticket sits in a queue for a while

  • The desk operator calls the next customer by clicking a button. The system determines which is the next ticket suitable for the desk (given that not all desks provide the same services)

  • Once the ticket is called to a desk, there is an event outputed and a display system shows the ticket number along with the desk where the customer should go

  • Once the customer arrives at the desk, the operator clicks another button so signal the beginning of the actual providing of the service

  • Once the service has been provided and the customer leaves, the desk operator clicks yet another button, the ticket is archived and the desk becomes free again. Until the operator calls the next ticket.

There is a central server and 2 types of client apps : the ticket distributor terminal, and the desk GUI client.

So in terms of REST resources, one of the main resources is the ticket. First, a new ticket is created (POST), and it is attributed a number. That is easy.

But then I don't know how to model the transitions to the ticket state that are initiated by the desk client, since they are not just updates of the ticket but trigger business logic that is executed on the server and has side-effects not only on the ticket :

  • Call next ticket (causes a state change on the server, the ticket is taken from the queue, associated with the desk that called it and marked as "being called")
  • Signal the server that the customer has arrived at the desk (causes a state change on the server, the ticket is marked as "being served", still associated with the desk)
  • Signal the server that the customer has leaved (causes a change on the server, the ticket is separated from the desk, marked as "served", put into a collection of all the served tickets, and a log entry is written to the DB)
  • Signal the server that the customer has not arrived (no show) and the ticket is canceled (causes a change on the server, the ticket is separated from the desk, put into a collection of all the canceled tickets, marked as "not served", and a log entry is written to the DB)

Etc.

All those operations should be performed in an atomic way too, and synchronously.

They map nicely to "actions", i.e. RPC, and that's the way it was implemented in the previous versions of the system (namely RMI calls). For example, the desk can "call the next ticket" (an action), and he gets back the next matching ticket.

How to model them in a RESTful way ?

Also, is it worth it, or should I simply be a little flexible on the RESTfulness and model them as "actions" or something similar (so basically RPC over HTTP) ?

Thanks for your thoughts !

  • Did you rethink it where you put the Meeting as the resource instead of the Ticket? Then it seems quite doable: Create meeting with a service ID, update meeting when a desk gets assigned, update status when meeting is complete. The ticket is just a view on that resource. – Luc Franken Jan 27 '17 at 10:44
  • @Luc : interesting thought, but that would probably mean heavy refactoring/rewriting of my internal logic whic is already heavily based around tickets. It could use some simplification anyway. I'll think about it. – Pierre Henry Jan 27 '17 at 11:10
3

All operations you are mentioning are changes to the state of the ticket, so why not model them as operations on Ticket resource?

  1. Customer registration client creates new ticket: POST /tickets
  2. Desk client updates list of tickets, assigning employee to ticket with PATCH request to /tickets/next and JSON body { "employee_id" : "5" }. Response will be HTTP 301 with Location header pointing to specific ticket: /tickets/172 This request will also send an async message to following subscribers:
    • display service, which shows the queue status on monitors in the room, to notify that queue has been updated and clients shall retrieve new contents.
    • call expiration job, which will update the ticket if customer will not arrive in given time.
  3. If customer arrives at the desk, desk client sends PATCH request to specific ticket /tickets/172, which updates ticket status and sends async message to the mentioned subscribers. In this case, call expiration job will be dismissed for this ticket upon receiving the message.
  4. Upon serving the ticket, desk client will perform PATCH request to update ticket status.
  5. If customer does not arrive within given time, call expiration job will perform PATCH request to the URI provided in async message before to update ticket status. During processing of this request async message to display service will remove the ticket from the queue display.

All these operations are atomic from client's perspective and details of implementation of atomic operations shall not be exposed. Regarding the logs, they are necessary to maintain API SLA and not as a business function, so they are in fact implementation detail which is not known to the clients and it's safe not to consider logs as side effects.

  • +1 This is probably the best way to go. Problem in my case is that it doesn't match the way the business logic is implemented internally, which kind of moves tickets around instead of the tickets having a state field that can be updated. But I think it is the opportunity to also rework this internal logic and simplify it. The result will be cleaner and less impedance between REST API and internal implementation. – Pierre Henry Jan 27 '17 at 15:15
  • Ticket state can be a property of public model, which is not necessarily the same as internal model. It of course requires additional effort to translate state changes into "moving a ticket" (not sure, how it may work), but still possible from the design perspective. You just need to estimate efforts to choose one of the solutions for short and long term. – Ivan Gammel Jan 27 '17 at 15:29
  • Yes you are right. In that case I decided that I will change the way the internal implementation works because it makes sense and the resulting code will be cleaner. – Pierre Henry Jan 27 '17 at 16:40
1

Just RPC it :-)

Don't try shoehorning everything to a model of 'resources'. The parameters an action has you can consider being a 'resources'. Even if you could, doesn't means you should.

When using RPC try to avoid becoming too chatty and make your actions fully contained, atomic, one invocation operations that don't require immediate follow up.

So don't dequeue a ticket in one call and then assign it to a desk in another. Invoke an action which will do both, e.g. 'assign-new-ticket-to-desk'. Leaving the system in a consistent state after.

  • +1 for the fully contained, atomic, one invocation operations, that's the way it already worked with RPC – Pierre Henry Jan 27 '17 at 10:32

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.