Case study
Car Rental Management System
Seven modules over one relational core, with the rules that matter — above all, no car booked twice for the same dates — enforced by the database itself rather than trusted to the UI.
The problem
A rental company's data is all constraints: a car cannot be on two contracts at the same time, a contract cannot end before it starts, a customer needs a valid licence. Enforce those only in C# and they hold until the day someone writes to the database another way.
What I did & decided
- Wrote down the business rules (19 of them, in
docs/business_rules.md) and modelled the domain in a normalised 12-table PostgreSQL schema: people, customers, employees, licences, cars and categories, contracts, payments, discounts, penalties and returns. - Put the overlap check in the database: a
BEFORE INSERTand aBEFORE UPDATEtrigger callfn_car_availableand reject any contract whose dates collide with an existing one for the same car. - Added an after-return trigger that updates the car's mileage and maintenance state, plus stored functions for a customer's active contracts and a car's age.
- Built seven MVC modules on top (cars, customers, employees, contracts, payments, licences, accounts) with ASP.NET Core Identity and role-based access.
- Committed the SQL scripts in order (
01_tables→05_seed_data) with an ERD and a requirements document, so the schema can be reviewed without reading any C#.
What came of it
- The rules survive a direct write: anything that bypasses the app still meets the same triggers.
- A double booking fails with a readable error naming the car and the dates, instead of becoming a support call.
- A reviewer can read the schema on its own, which is how a reviewer actually reads.
docs folder.Next case: Campusly