The Coffee Shop Problem

I was asked to solved this problem a couple of times. The goal is to create a concurrency model for a coffee shop. I always answered with something like this,

This is an application of the Producer Consumer Pattern.

  • Orders go through the cashier and the cashier puts orders in a queue
  • A bunch of baristas take from the order queue, prepare it and put the cup in the coffee machine. Only one Barista can use the machine so it should form some sort of queue.
  • Once the coffee is done, it will be placed in a queue. A bunch of baristas will take coffee from this queue to be do some finishing touches, like adding creamer, sugar, sprinkles, and then shout "Coffee for John".

The diagram looks like a real coffee shop layout. This models the real world, OOP zeolots would be proud. The person who gave me the problem looks impressed. At that time, I thought this made sense. Except that it doesn't.

Yes, it looks like the real world, but there are differences between the real world and code. In the real world, you can't magically add a barista, or a coffee machine to scale up. The real world is complicated, and I think we shouldn't be bound by the limitations of the real world when writing code. Here is my solution now.

The orders come in through a queue, and then a coffee shop consumes the order. The smaller components like barista and coffee machine still interact but they will do it in one stack to perform an atomic command. Notice that the cashier is still left as a producer. Payment could take time, at least in my experience. So while the customer is waiting for change, or credit processing, the order can already be placed.

Advantages

  • Less overhead. Synchronization is expensive. pushing and taking from a queue requires synchronization. This is worsened when people start implementing mircoservices modeled from the previous diagram. This requires message queuing which probably communicate via sockets or something other mechanism. In the second diagram, we remove unecessary queuing.
  • Cleaner Code. It's obviously easier to implement.
  • Easier Scaling. Unlike the real world, we CAN magically spin up a coffee shop. All you need are more threads, memory, or if needed, more servers.

Conclusion

Aiming for a simpler solution can save time and headache.

PS. Concurrency is hard. This might sound basic to some but it took me a while to realize my previous mistakes. Comments would be highly appreciated.







Interesting idea. Have you tried running benchmark tests for the different approaches? It would be great to see the difference in execution time and throughput, especially when you scale up the load you have to process. I do agree that it's easier to implement. As another benefit, I suppose it's easier to maintain and debug, something that becomes a more valuable attribute of code over time. For the last advantage you mentioned, I think scaling is achievable by both of the approaches. Still, a good read! Hope to see more thoughts on design patterns from you :)

To view or add a comment, sign in

More articles by Karlo Serrano

  • Self-documenting code. Is it a thing?

    This is not to discuss the importance of readability and how to achieve it. This is to discuss if documentation is…

  • Where to put CSS files

    I have always tried my best to follow Uncle Bob's Clean Architecture in every piece of code I write. The main thought…

Explore content categories