Java Scenario-Based Interview Question 4: Design Food Delivery System
In the fast-paced world of food delivery, customers demand flexibility, personalization, and reliability. Building a system that supports…
Java Scenario-Based Interview Question 4: Design Food Delivery System
In the fast-paced world of food delivery, customers demand flexibility, personalization, and reliability. Building a system that supports multiple payment methods, dynamic discounts, and split payments while maintaining scalability and simplicity is no small feat. In this blog, we’ll explore how to design such a system using best practices and design patterns.

The Problem Statement
Imagine a food delivery application where customers can order food from restaurants. The system processes the order, applies discounts, and handles payments. However, the requirements are far from simple:
- Multiple Payment Methods: Customers can pay using wallets, credit cards, or cash. They can also split payments across these methods.
- Dynamic Discounts:
- 10% discount on weekends.
- Additional 5% discount for customers over 50 years old.
- Another 5% discount if the order is placed on the customer’s birthday.
-
Error Handling: The system must validate payment details and gracefully handle payment failures.
-
Scalability: The solution must be modular, maintainable, and scalable.
The Approach
To tackle this problem, we’ll use a combination of design patterns and best practices:
- Strategy Pattern: To handle multiple payment methods.
- Decorator Pattern: To apply dynamic discounts.
- Factory Pattern: To create payment processors and discount decorators.
We’ll break the solution into three main components:
- Order Service: Manages the order lifecycle, including discount calculation and payment processing.
- Discount System: Applies dynamic discounts based on customer and order details.
- Payment Processor: Handles multiple payment methods and split payments.
Step-by-Step Solution
Let’s dive into the implementation of each component and explain how the code is production-grade.
1. Order Service
The DefaultOrderService is the entry point for placing an order. It is responsible for:
- Calculating Discounts: Applies all applicable discounts to the order.
- Validating Payments: Ensures that the split payment amounts match the total order amount.
- Processing Payments: Delegates payment handling to the
PaymentProcessor.
Key Features of Production-Grade Code:
- Separation of Concerns: The service delegates discount calculation and payment processing to specialized components, ensuring modularity.
- Error Handling: The service gracefully handles exceptions and provides meaningful error messages.
- Scalability: The design allows for easy addition of new discounts or payment methods without modifying existing code.
Here’s the implementation:
public class DefaultOrderService implements OrderService {
private final List<PriceDecorator> discountDecorators;
private final PaymentProcessor paymentProcessor;
public DefaultOrderService(PaymentProcessor paymentProcessor, List<PriceDecorator> discountDecorators) {
this.paymentProcessor = paymentProcessor;
this.discountDecorators = discountDecorators;
}
@Override
public DiscountedOrder placeOrder(Order order) {
try {
// Step 1: Calculate final price with discounts
List<String> discountBreakdown = new ArrayList<>();
Float discountedPrice = calculateFinalPrice(order.getTotalPrice(), order, LocalDate.now(), discountBreakdown);
// Step 2: Validate payment details
validatePaymentDetails(order.getPaymentDetails(), discountedPrice);
// Step 3: Process payments
paymentProcessor.processPayments(order.getPaymentDetails(), order.getCustomer(), discountedPrice);
// Step 4: Return the discounted order
return new DiscountedOrder(order, discountedPrice, discountBreakdown);
} catch (Exception e) {
throw new OrderProcessingException("Error processing order", e);
}
}
private Float calculateFinalPrice(Float basePrice, Order order, LocalDate orderDate, List<String> discountBreakdown) {
Float finalPrice = basePrice;
for (PriceDecorator decorator : discountDecorators) {
Float newPrice = decorator.calculatePrice(finalPrice, order.getCustomer(), orderDate);
if (!newPrice.equals(finalPrice)) {
discountBreakdown.add(decorator.getClass().getSimpleName() + " applied: " + (finalPrice - newPrice));
}
finalPrice = newPrice;
}
return finalPrice;
}
private void validatePaymentDetails(PaymentDetails paymentDetails, Float orderTotal) {
if (!paymentDetails.getTotalAmount().equals(orderTotal)) {
throw new OrderProcessingException("Split payment amounts do not match the order total.");
}
}
}
2. Discount System
The discount system uses the Decorator Pattern to apply dynamic discounts. Each discount is implemented as a PriceDecorator. This approach allows us to add or remove discounts without modifying the core logic.
Why the Decorator Pattern?
The Decorator Pattern allows us to dynamically add behavior (discounts) to an object (order price) without modifying its structure. Each discount is encapsulated in its own class, making the system modular and easy to extend.
Key Features of Production-Grade Code:
- Modularity: Each discount is implemented as a separate class, making it easy to add or remove discounts.
- Reusability: Discounts can be reused across different services or contexts.
- Extensibility: New discounts can be added without modifying existing code.
Implementation of Discounts
Weekend Discount:
class WeekendDiscount implements PriceDecorator {
@Override
public Float calculatePrice(Float basePrice, Customer customer, LocalDate orderDate) {
if (orderDate.getDayOfWeek() == DayOfWeek.SATURDAY || orderDate.getDayOfWeek() == DayOfWeek.SUNDAY) {
return basePrice * 0.9f; // 10% discount
}
return basePrice;
}
}
Age Discount:
class AgeDiscount implements PriceDecorator {
@Override
public Float calculatePrice(Float basePrice, Customer customer, LocalDate orderDate) {
if (customer.getAge() > 50) {
return basePrice * 0.95f; // Additional 5% discount
}
return basePrice;
}
}
Birthday Discount:
class BirthdayDiscount implements PriceDecorator {
@Override
public Float calculatePrice(Float basePrice, Customer customer, LocalDate orderDate) {
if (customer.getBirthday().equals(orderDate)) {
return basePrice * 0.95f; // Additional 5% discount
}
return basePrice;
}
}
3. Payment Processor
The DefaultPaymentProcessor handles multiple payment methods and validates split payments. It ensures that the total split amount matches the order total and processes each payment method sequentially.
Key Features of Production-Grade Code:
- Validation: Ensures that the total split payment matches the order total before processing payments.
- Error Handling: Throws meaningful exceptions for invalid or failed payments.
- Extensibility: New payment methods can be added without modifying the existing processor.
Here’s the implementation:
public class DefaultPaymentProcessor implements PaymentProcessor {
@Override
public void processPayments(PaymentDetails paymentDetails, Customer customer, Float orderTotal) {
if (!paymentDetails.getTotalAmount().equals(orderTotal)) {
throw new PaymentProcessingException("Split payment amounts do not match the order total.");
}
List<PaymentMethod> methods = paymentDetails.getPaymentMethods();
List<Float> amounts = paymentDetails.getAmounts();
for (int i = 0; i < methods.size(); i++) {
if (!methods.get(i).pay(amounts.get(i), customer)) {
throw new PaymentProcessingException("Payment failed");
}
}
}
}
You can find full code here: https://github.com/raju4789/code-katas/tree/main/src/main/java/com/raju/codekatas/orderingsystem
👋 Let’s Connect!
If you found this post insightful, here’s how you can help spread the knowledge:
👏 Clap if you enjoyed it — your claps motivate me to keep sharing more Java tricks and insights! 🔗 Share this post with your network so others can learn too. 💬 Ask your questions or share your experiences in the comments. Have you encountered this situation? Let’s discuss!
🚀 Follow me for more deep dives into Java, design patterns, and programming gotchas! Let’s learn and grow together. 🌟
메타데이터
- post_id
- bb4454abe599
- slug
- java-scenario-based-interview-question-4-design-food-delivery-system-bb4454abe599
- url
- https://medium.com/@narasimha4789/java-scenario-based-interview-question-4-design-food-delivery-system-bb4454abe599
- canonical_url
- https://medium.com/@narasimha4789/java-scenario-based-interview-question-4-design-food-delivery-system-bb4454abe599
- author_url
- https://medium.com/@narasimha4789
- status
- ok
- fetched_at
- 2026-08-06 07:53:32