Skip to content
intermediatePhase 50 · LLD Practice

Coffee Machine

Design a coffee machine with drink selection and customization.

1h 30m
0 problems
Topic Progress0%

Requirements

Functional Requirements

1. Beverage Selection:
   - Display available beverages
   - Select beverage type

2. Customization:
   - Size (Small, Medium, Large)
   - Add-ons (Milk, Sugar, Cream, Flavor shots)
   - Temperature (Hot, Iced)

3. Ingredient Management:
   - Track ingredient levels
   - Low ingredient alerts

4. Payment:
   - Cash/Coin
   - Card
   - Mobile payment

5. Dispensing:
   - Prepare beverage
   - Add ingredients in correct order
   - Dispense when complete

Core Entities

CoffeeMachine, Beverage, Ingredient,
Order, Payment, Size, AddOn

Beverage Types

├── Espresso
│   ├── Americano
│   ├── Latte
│   └── Cappuccino
├── Drip Coffee
├── Tea
│   ├── Green Tea
│   └── Black Tea
└── Hot Chocolate

Beverage Selection

Beverage Interface

public interface Beverage {
    String getName();
    double getBasePrice();
    Map<Ingredient, Integer> getIngredients();
    List<AddOn> getAvailableAddOns();
}

Concrete Beverages

public abstract class CoffeeBeverage implements Beverage {
    protected final String name;
    protected final double basePrice;
    protected final Map<Ingredient, Integer> ingredients;
    
    @Override
    public Map<Ingredient, Integer> getIngredients() {
        return new HashMap<>(ingredients);
    }
}

public class Latte extends CoffeeBeverage {
    public Latte() {
        super("Latte", 4.50);
        ingredients.put(Ingredient.ESPRESSO, 2);
        ingredients.put(Ingredient.MILK, 3);
        ingredients.put(Ingredient.MILK_FOAM, 1);
    }
    
    @Override
    public List<AddOn> getAvailableAddOns() {
        return List.of(AddOn.CARAMEL, AddOn.VANILLA, 
                      AddOn.SOY_MILK, AddOn.OAT_MILK);
    }
}

public class Americano extends CoffeeBeverage {
    public Americano() {
        super("Americano", 3.50);
        ingredients.put(Ingredient.ESPRESSO, 2);
        ingredients.put(Ingredient.WATER, 4);
    }
}

Size and Pricing

public enum Size {
    SMALL(0.8, 8),
    MEDIUM(1.0, 12),
    LARGE(1.3, 16);
    
    private final double priceMultiplier;
    private final int volumeOz;
}

public class PriceCalculator {
    public double calculate(Beverage beverage, Size size, List<AddOn> addOns) {
        double price = beverage.getBasePrice() * size.getPriceMultiplier();
        price += addOns.stream()
            .mapToDouble(AddOn::getPrice)
            .sum();
        return Math.round(price * 100.0) / 100.0;
    }
}

Order Builder

public class OrderBuilder {
    private Beverage beverage;
    private Size size = Size.MEDIUM;
    private final List<AddOn> addOns = new ArrayList<>();
    
    public OrderBuilder beverage(Beverage beverage) {
        this.beverage = beverage;
        return this;
    }
    
    public OrderBuilder size(Size size) {
        this.size = size;
        return this;
    }
    
    public OrderBuilder addOn(AddOn addOn) {
        addOns.add(addOn);
        return this;
    }
    
    public Order build() {
        double price = new PriceCalculator()
            .calculate(beverage, size, addOns);
        return new Order(beverage, size, addOns, price);
    }
}

// Usage
Order order = new OrderBuilder()
    .beverage(new Latte())
    .size(Size.LARGE)
    .addOn(AddOn.CARAMEL)
    .addOn(AddOn.SOY_MILK)
    .build();

Customization

AddOn System

public enum AddOn {
    CARAMEL(0.50),
    VANILLA(0.50),
    MOCHA(0.50),
    SOY_MILK(0.70),
    OAT_MILK(0.70),
    EXTRA_SHOT(0.75),
    WHIPPED_CREAM(0.00),
    CINNAMON(0.00);
    
    private final double price;
}

Beverage Preparation

public class BeveragePreparer {
    private final IngredientDispenser dispenser;
    
    public void prepare(Order order) {
        // 1. Get base ingredients
        Map<Ingredient, Integer> ingredients = 
            order.getBeverage().getIngredients();
        
        // 2. Scale by size
        double multiplier = order.getSize().getPriceMultiplier();
        
        // 3. Add extra ingredients from add-ons
        for (AddOn addOn : order.getAddOns()) {
            addIngredientsForAddOn(ingredients, addOn);
        }
        
        // 4. Dispense each ingredient
        for (Map.Entry<Ingredient, Integer> entry : ingredients.entrySet()) {
            int amount = (int) (entry.getValue() * multiplier);
            dispenser.dispense(entry.getKey(), amount);
        }
        
        System.out.println("Beverage ready: " + order.getBeverage().getName());
    }
    
    private void addIngredientsForAddOn(
            Map<Ingredient, Integer> ingredients, AddOn addOn) {
        switch (addOn) {
            case EXTRA_SHOT:
                ingredients.merge(Ingredient.ESPRESSO, 1, Integer::sum);
                break;
            case SOY_MILK:
                ingredients.put(Ingredient.MILK, 0); // Replace
                ingredients.put(Ingredient.SOY_MILK, 3);
                break;
        }
    }
}

Ingredient Inventory

public class IngredientInventory {
    private final Map<Ingredient, Integer> stock;
    private final int lowStockThreshold;
    
    public boolean hasIngredients(Map<Ingredient, Integer> required) {
        return required.entrySet().stream()
            .allMatch(e -> stock.getOrDefault(e.getKey(), 0) >= e.getValue());
    }
    
    public void deduct(Map<Ingredient, Integer> used) {
        used.forEach((ingredient, amount) -> {
            stock.merge(ingredient, -amount, Integer::sum);
            if (stock.get(ingredient) <= lowStockThreshold) {
                notifyLowStock(ingredient);
            }
        });
    }
    
    private void notifyLowStock(Ingredient ingredient) {
        // Observer: alert for restocking
    }
}

Follow-ups

Follow-up Questions

1. How to handle multiple drinks in one order?
   → Queue of orders
   → Sequential preparation
   → Order status tracking

2. How to handle loyalty program?
   → Points per purchase
   → Free drink redemption
   → Member pricing

3. How to handle custom recipes?
   → Recipe builder interface
   → Save custom recipes
   → Share recipes

4. How to handle maintenance?
   → Self-cleaning cycle
   → Bean grinding calibration
   → Water filter replacement

5. How to handle mobile ordering?
   → API for remote ordering
   → Queue management
   → Ready notification

Design Patterns

Pattern Usage
Builder Order construction
Strategy Pricing calculation
Observer Low stock alerts
Template Method Beverage preparation
Factory Beverage creation

Machine Display

┌──────────────────────────────────────┐
│         COFFEE MACHINE               │
├──────────────────────────────────────┤
│  Select Beverage:                    │
│  1. Espresso      $3.50             │
│  2. Latte         $4.50             │
│  3. Cappuccino    $4.50             │
│  4. Americano     $3.50             │
│  5. Green Tea     $3.00             │
├──────────────────────────────────────┤
│  Size: [S] [M] [L]                   │
│  Add-ons: [Extra Shot] [Soy Milk]   │
│  Temperature: [Hot] [Iced]           │
├──────────────────────────────────────┤
│  Total: $5.70                        │
│  Insert payment                      │
└──────────────────────────────────────┘

Practice Problems

0/3solved
Design Coffee Machine System

Design a scalable Coffee Machine system. Cover high-level architecture, data model, and API design.

Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliability
Coffee Machine Scaling

How would you scale Coffee Machine to handle 10x the current load? Identify bottlenecks and solutions.

Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decomposition
Coffee Machine Failure Modes

Analyze potential failure modes for Coffee Machine and design mitigation strategies.

Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradation

Quiz

1. What pattern is used for order construction?

Question 1 options

2. How does size affect pricing?

Question 2 options

3. How are add-ons handled in ingredient dispensing?

Question 3 options

4. What triggers a low ingredient alert?

Question 4 options

5. How does the Template Method pattern apply?

Question 5 options

Flashcards

Question

What pattern builds coffee orders?

Answer

Builder pattern. Step by step: select beverage → choose size → add-ons → build order.

Question

How is pricing calculated?

Answer

Base price × size multiplier + add-on prices. Size: Small=0.8, Medium=1.0, Large=1.3.

Question

How are add-ons applied?

Answer

Add-ons add extra ingredients (extra shot) or replace ingredients (soy milk replaces regular milk).

Question

Ingredient inventory management?

Answer

Track stock levels. Check availability before preparation. Deduct after dispensing. Alert on low stock.

Question

Beverage preparation flow?

Answer

Template: 1) Get base ingredients, 2) Scale by size, 3) Add customization, 4) Dispense each ingredient.

Revision Notes

Key Takeaways

  • 1.Builder pattern constructs orders step by step with beverage, size, and add-ons
  • 2.Size multiplier scales both price and ingredient amounts
  • 3.Add-ons either add extra or replace existing ingredients
  • 4.Ingredient inventory tracks stock and alerts on low levels
  • 5.Template Method pattern standardizes beverage preparation flow

Interview Tips

  • Show Builder pattern for order construction
  • Explain how size affects both pricing and ingredients
  • Discuss ingredient dispensing with add-on handling
  • Mention mobile ordering as extension

Cheat Sheet

Coffee Machine - Cheat Sheet

Order Flow:

  1. Select beverage type
  2. Choose size (S/M/L)
  3. Add customizations
  4. Calculate price
  5. Process payment
  6. Prepare and dispense

Pricing:
Base Price × Size Multiplier + Add-ons

Ingredient Flow:

  1. Get base ingredients
  2. Scale by size
  3. Apply add-ons
  4. Dispense ingredients
  5. Deduct from inventory

Patterns:
Builder (orders), Strategy (pricing), Observer (stock alerts), Template Method (preparation)