This is to create a simple shopping cart system for a retail store selling clothes, e.g. T-shirts and Jeans. In this project only minimal design, feature and domain modelling will be considered, to satisfy the focused use cases.
- Able to add products into shopping cart.
- Able to view total sum of products added in shopping cart.
- Allow adding coupons for getting various discount (e.g. Buy X Free Y, Buy X sets and get Y% discount on Z set)
Shopping Cart : Each customer own a shopping cart. Serves as Aggregate Root in this context.
Public method | Description | Real life usage |
---|---|---|
Add Items | Add selected product(s) into shopping cart | Call by ShoppingCartService (not included in project). ShoppingCartService will receive selected product(s) from either API call or listening to domain event raised (e.g. from Catalog service) |
Cart Item : Serve as record added into shopping cart. No public method exposed.
Problem: How/ where should we validate and ensure valid domain model creation/ initialization? There are TWO schools of thought:
- Domain model should always be valid - by ensuring validation is done before creation, e.g. using Factory pattern.
- Domain model should ensure itself to be valid - it is the responsibility of domain model to do self validation, e.g. add validation logic in ctor.
Decision:
- In this case, the chosen way is โ๏ธ 2. Domain model should ensure itself to be valid.
- By extracting the validation logic in its own method and using base abstract class, SelfValidationDomainModel.cs to enforce running Validation method for every constructor calls.
Problem: Is it the responsibility of shopping cart to calculate total price of added items/ products?
Decision:
- To make this context simple, it is decided to have shopping cart to be responsibile for the price calculation.
- In the future, if there are different formula of calculation, the calculation method can be extracted to Calculator interface. If such decision is being made, it might be a good idea to consider using Factory to create shopping cart with selected calculator class.
- Introduced interface IDiscountableCoupon with calculate discounted amount can be renamed as IDiscountableItem.
- Ideally Coupon should be an abstract class implementing IDiscountableCoupon. In the future there can be different kinds of coupon (e.g. BuyXFreeYCoupon, BuySetsGetXPercentDiscountCoupon, etc.) inherit this abstract class and override with different formula.
- Add the omitted self validation check in all coupon classes.
- Group discount or coupon into dedicated folder.
- Current code will calculate coupon based on FIFO, adding a order/ priority field to coupon can be a good idea. Another way is to have the ability to calculate the lowest price for customers.