From requirements to classes
Part of the free Low-Level Design course on LogicWiz, in the chapter: How LLD interviews work. The class-diagram exercises run in the app.
Week 1, Day 3: Coupons from one paragraph
"You made a class called CouponManager. Tell me in one sentence what it manages."
Priya, reading Arjun's design
Ask before you draw
The product manager's note for SwiftKart's new coupon feature is one paragraph long:
"A customer can apply a coupon code to the cart. A coupon gives either a flat discount or a percentage discount. It works only if the cart total is above a minimum, and only until its expiry date."
Arjun started drawing classes at once. Priya stopped him. A requirement this short hides many decisions, and in an interview you are expected to find them. You find them with clarifying questions: questions that turn a vague prompt into a scope you can build in 90 minutes.
- Can a cart hold more than one coupon at a time?
- Does a percentage coupon have a cap, such as "10% off, up to ₹150"?
- Can the same customer use a coupon twice?
- Is the minimum checked on the item total, or on the total after delivery fees?
- Do we need to create coupons in this round, or only apply them?
Arjun wrote the answers at the top of his file. One coupon per cart. Percentage coupons have a cap. One use per customer. The minimum is checked on the item total. Coupons are loaded from a fixed list.
Each answer removes a branch from the design. "One coupon per cart" means Cart holds a single Coupon field, not a list of them.
Sometimes the interviewer answers "you decide". Then pick the simpler option, say it out loud, and keep the design open for the other option. That shows judgement, which is part of the grade.
Nouns become classes, verbs become methods
Now read the paragraph again. Mark the nouns (things) and the verbs (actions).
- Nouns: customer, coupon code, cart, coupon, flat discount, percentage discount, cart total, minimum, expiry date.
- Verbs: apply, give, works only if.
This is a common first step. Each important noun is a candidate class. Each verb is a candidate method on the class that does the action. A candidate is an option to check, not a final answer.
So the first draft has Customer, Cart and Coupon. It has cart.applyCoupon(coupon) and coupon.discountFor(total). Notice that the method discountFor sits on Coupon, because the coupon holds the data needed to work out the discount.
Verbs can also point at the wrong class. The sentence says "a customer applies a coupon", which suggests customer.applyCoupon(). But the customer object holds a name and a phone number, not the cart total. The cart holds the items and the total, so the method belongs on Cart. Put a method on the class that owns the data it uses, not on whoever is the subject of the sentence.
Where the noun rule fails
Arjun turned every noun into a class. He got nine boxes, including CartTotal, Minimum and ExpiryDate. Priya showed him three ways the rule fails.
Some nouns are fields, not classes. "Minimum", "expiry date" and "coupon code" are values that belong to a coupon. They become fields of Coupon: minOrder, expiresAt and code. A noun earns its own class when it has behaviour, or several fields of its own.
Some nouns are kinds of one thing. "Flat discount" and "percentage discount" are two kinds of coupon that work out the discount differently. That is one idea with two behaviours. Module 2 handles it with polymorphism.
Some classes are not nouns in the text. Somebody must find a coupon by its code, but no noun in the paragraph says so. You add a CouponRepository because the design needs it. A repository is a class that stores objects and finds them, here in a map in memory.
Hidden verbs matter too. "Works only if" is a check. It becomes a method: coupon.isValidFor(cartTotal, today).
⚠️ Warning: A class named
Manager,HelperorUtiloften means nobody decided who owns a job. Ask "which object has the data this method needs?" and put the method there.
One responsibility per class
Here is Arjun's first CouponManager. Look at how many things it knows about.
public class CouponManager {
private final Map<String, String[]> rows = new HashMap<>();
public int apply(String code, int cartTotal, String userId, LocalDate today) {
String[] r = rows.get(code); // type, value, min, expiry, cap
if (r == null) throw new IllegalArgumentException("No coupon");
if (LocalDate.parse(r[3]).isBefore(today)) throw new IllegalStateException("Expired");
if (cartTotal < Integer.parseInt(r[2])) throw new IllegalStateException("Below minimum");
int value = Integer.parseInt(r[1]);
int off = r[0].equals("FLAT") ? value
: Math.min(cartTotal * value / 100, Integer.parseInt(r[4]));
sendSms(userId, "You saved Rs " + off);
return cartTotal - off;
}
private void sendSms(String userId, String text) {
// calls the SMS gateway
}
}
It stores coupons, parses text rows, checks expiry, checks the minimum, works out the discount and sends an SMS. That gives it six reasons to change. A responsibility is one job that one group of people might ask you to change. The single responsibility principle says each class should have one responsibility. Module 4 covers it in depth.
The fix puts each job next to the data it needs.
public class Coupon {
private final String code;
private final long minOrderPaise;
private final LocalDate expiresAt;
private final long flatOffPaise;
public Coupon(String code, long minOrderPaise, LocalDate expiresAt, long flatOffPaise) {
this.code = code;
this.minOrderPaise = minOrderPaise;
this.expiresAt = expiresAt;
this.flatOffPaise = flatOffPaise;
}
public boolean isValidFor(long cartTotalPaise, LocalDate today) {
return !today.isAfter(expiresAt) && cartTotalPaise >= minOrderPaise;
}
public long discountFor(long cartTotalPaise) {
return Math.min(flatOffPaise, cartTotalPaise);
}
}
Now Coupon decides about coupons. CouponRepository finds them by code. Cart holds one coupon and asks it for the discount. Sending an SMS moves to a notification class that knows nothing about discounts. Money is kept in paise, as a long, so there are no rounding errors from decimal numbers.
Try it: draw the coupon design
The board below has a Customer and a Cart. Add a Coupon class with the fields and the method it needs, and connect the cart to it. Click a class to edit it. Module 3 explains the notation in full.
📌 Summary: Ask clarifying questions first. Nouns suggest classes and verbs suggest methods, but some nouns are only fields and some classes are missing from the text. Give each class one job.
What Arjun learns next
Arjun's Coupon now guards its own rules. But what stops other code from changing a field such as expiresAt directly? Next week: encapsulation and abstraction, using SwiftKart's wallet.
Check your understanding
- The requirement says "A customer books a delivery slot. Each slot has a start time and a capacity." Which noun is most likely a field, not a class?
- Arjun's CouponManager loads coupons, checks expiry, works out discounts and sends an SMS. What is Priya's main concern?
- No noun in the coupon paragraph says how a coupon is found from the code the customer types. What should Arjun do?
- The text says "a customer applies a coupon". Where should the applyCoupon method go?