Skip to content
Low Level Design Mastery Logo
LowLevelDesign Mastery

Design a Coffee Machine

Design an automated beverage dispenser with inventory tracking and state management.

Design a coffee machine system that can prepare various beverages (coffee, tea, cappuccino, latte) based on predefined recipes, manage ingredient inventory (coffee beans, milk, water, sugar), allow user customization (sugar level, milk type), monitor ingredient levels with alerts, and handle the dispensing process with proper state management.

In this problem, you’ll design a system that handles multiple beverage types, user customizations, and ensures the machine remains in a valid state throughout the brewing process.


Design a coffee machine that prepares various drinks based on recipes, tracks ingredient levels, and provides a seamless user experience through state-aware operations.

Functional Requirements:

  • Beverage Variety: Support multiple types (Coffee, Tea, Cappuccino, Latte) using the Factory Pattern.
  • Inventory Management: Track ingredients (beans, milk, water, sugar) with real-time quantity tracking.
  • Recipe System: Each beverage has a predefined recipe; validate availability before starting.
  • Customization: Allow users to select sugar level (None to High) and milk type (Regular, Skim, Almond).
  • Alert System: Monitor ingredient levels and notify when they fall below a threshold (e.g., 20%).
  • State Management: Manage operational states (Idle, Preparing, Dispensing, Maintenance).
  • Preparation Flow: Handle the full lifecycle: validate → consume → prepare → dispense → Idle.
  • Error Handling: Gracefully reject requests if validation fails or ingredients run out mid-brew.
  • Maintenance: Support refilling ingredients to restore inventory levels.

Non-Functional Requirements:

  • Thread Safety: Ensure inventory operations are thread-safe for concurrent preparation requests.
  • Modular Design: Each class should have well-defined roles following the Single Responsibility Principle.
  • Extensibility: Easy to add new beverages, ingredients, or customizations without refactoring.
  • Reliability: The core preparation logic and state management must be easy to test and maintain.

The system coordinates between the User Interface, the Inventory Manager, and the Brewing Engine.

Diagram
classDiagram
    class CoffeeMachine {
        -MachineState currentState
        -Inventory inventory
        +selectBeverage(type)
        +dispense()
    }
    
    class Inventory {
        -Map~Ingredient, Quantity~ stock
        +hasEnough(recipe)
        +consume(recipe)
    }
    
    class Beverage {
        <<abstract>>
        -Recipe recipe
        +prepare()*
    }

    class Coffee {
        +prepare()
    }
    
    class MachineState {
        <<interface>>
        +handleAction()
    }

    CoffeeMachine --> MachineState
    CoffeeMachine --> Inventory
    Beverage <|-- Coffee
    Beverage --> Recipe

Diagram

A coffee machine shouldn’t allow a user to select a drink while it’s already brewing or when it’s in a maintenance/error state.

Solution: Use the State Pattern. Define states like IdleState, BrewingState, DispensingState, and OutOfOrderState. Each state object defines what actions are allowed, preventing invalid transitions.

The system needs to alert someone when beans or milk are low, but the Inventory shouldn’t be tightly coupled to a notification system.

Solution: Use the Observer Pattern. The Inventory acts as a Subject, and various AlertSystems (Email, Console, LED) act as Observers that get notified when levels cross a threshold.

If two requests come in simultaneously, they might both see enough milk available but together exceed the stock.

Solution: Implement Thread-Safe Inventory. Use locks or atomic operations (like ConcurrentHashMap and AtomicInteger) to ensure that checking and consuming ingredients is an atomic operation.


By solving this problem, you’ll master:

  • State Management - Controlling complex object lifecycles.
  • Inventory Logic - Managing resource allocation and tracking.
  • Decoupling - Using Observers to separate logic from notifications.
  • Factory Method - Dynamically creating objects based on configuration.

Ready to see the full implementation? Open the interactive playground to access:

  • 🎯 Step-by-step guidance through the 8-step LLD approach
  • 📊 Interactive UML builder to visualize your design
  • 💻 Complete Code Solutions in Python, Java, C++, TypeScript, JavaScript, C#
  • 🤖 AI-powered review of your design and code

After mastering Coffee Machine, try these similar problems: