note: currently, website is down, I'll fix it soon
There are a lot of apps made for food delivery for big cities, but not a lot of them are made for the small cities.
The article covers how the app is made, its structure, common problems, and solutions.
Why article might be important to you?
Do you plan to make a delivery app, or you want to buy one, or are you interested in starting a delivery app?
Or you want to see how a problem can be solved using the web?
Then, this content is for you.
“Make sure you understand the fundamental principles, i.e., the trunk and big branches, before you get into the leaves/details, or there is nothing for them to hang on to.” - Elon Musk
Here is the app. Take a look.
There is a big chance that the app data will be populated by random users because access to almost every account is open to the public. Please don't populate the application with unrelated stuff. If you cannot log in, try to make a new account.
Phone number: +387 99/999-999
- Phone number: +387 22/222-222
- Email: firstname.lastname@example.org
- Password: 123123123
- Phone number: +387 33/333-333
- Email: email@example.com
- Password: 123123123
Phone number: +387 11/111-111
- on the main page are shown:
- top 3 foods, according to the selling/ratings
- all restaurants sorted according to the sales/ratings/sponsorship/availability
- cart for users
- additions for foods, free and paid additions
- multiple user addresses
- delivery/pick up as soon as possible or at the desired time
- delivery/pick up food
- 6 types of notifications for users; order is:
- not accepted
- in the making
- in delivery
- you can come to pick up the food
- ingredients of each dish
- one click for the switch of the food availability on the restaurant side
- complete CRUD of foods for restaurant
- food categories
- admin dashboard for sales overview, confirmations, sponsorships
- Laravel 6, Vue 2.0, Vuex, Vuetify, Socket.io (Laravel-echo), Node, Redis, MySQL, OAuth 2.0
- Webpush notifications implemented for users and restaurants
- Slack notifications for admins
- Caching of the majority of cachable resources - on back end and front end
- Backup 3-5 times/week
- Image watermarks
- No Laravel mix; advanced webpack configuration
- Full tested; ~800 tests (features and unit tests)
- Fully extendable and testable structure for algorithms for sorting the top 3 foods on the back end
- user/restaurant/admin SPAs separated
- HTTP 2.0 for lazy loading Vue components; production raw ~400 files
- Fully separated API and front end
- Custom artisan commands for production testing
- Easily extendable for multiple cities
- Easily connectable to the mobile app (separate API made with that in mind)
- Mobile-first design
Number of tables: 46
Problem: cart items should be seen from all the devices, so saving items in the cookies memory or browser database is not going to help because the user is going to check the cart from the various devices
Solution: complete cart concept is on the back end
Problem: a history of the transactions
Solution: same tables of the cart solution prefixed with finished
Problem: Multiple user addresses (so the user can type in address once and access it anytime with ease)
Solution: home_address on main user table, and user_address table; one-to-many relationship
Problem: cart also should contain paid and free additions
Solution: shopping_cart, shopping_item, shopping_item_free_addition, shopping_item_paid_addition; one-to-many relationships
Problem: duplication of free/paid additions and ingredients
Solution: free/paid additions and ingredients link tables
Problem: some restaurants don't work on the weekends, but all of them work on weekdays on regular
Solution: separate table for weekends with one-to-one relationship
Problem: different delivery prices for the different locations
Solution: default_delivery_price on restaurant table and delivery table; one-to-many relationship
Problem: monthly restaurant fees
Solution: Laravel scheduler empowered with cron
Problem: food categories
Solution: separate table, one-to-many relationship
There are also Telescope tables, OAuth tables, notifications, failed_jobs, migrations, three types of users (user, restaurant, admin)...
- Put items with/without free/paid additions in the cart from the main page/restaurant page
- Go to the cart page
- Choose if you are coming for the food or desired address for delivery
- Choose if delivery/pick up is as soon as possible or at the desired time
After the successful order, the restaurant can either accept or decline the order.
If the 10 minutes pass and the restaurant still didn't accept or decline the order, the user can decline the order.
Declining from the restaurant can happen for various reasons, such as the restaurant forgetting to set unavailability to the dish.
Order process diagram, user side:
Order process diagram, restaurant side:
The history problem is easily solved. After the order is finished, the job is added to the Laravel queue, so its non-blocking.
What job will do is a transfer from the active carts to the finished carts, everything has finished table - cart, items and free/paid additions.
The problem that arises is changing the price of the additions/food in finished orders because finished items/additions are linked to the current info. The solution is to make a serialized snapshot of relevant data at a time. Not implemented in this system yet.
There are 6 queues required for the application to run correctly (250-350 MB of RAM).
All of them are powered through Redis.
- general queue
- queue for restaurant notifications
- queue for user notifications
- queue for admin notifications
- queue for processing images
The general queue is at the frontier for the most other queues and for the non-critical data.
Because this application is heavily notification dependent, there are 3 separate queues for the notifications, of which restaurant queue has high priority.
In the background of this application, 6 processes are running alternately.
- monthly fee event (calculate restaurants debts)
- hourly check which sponsorship is expired
- hourly remove old inactive shopping carts
- daily clean old backups
- daily backup database
- weekly backup files
Laravel framework is trying to be as secure as possible.
Every user input is checked both on the client and server-side.
Backup is done daily.
Many barriers to preventing DOS attacks, from the application side and at the OS.
Every form is done with a heavy focus on the details.
From the typing incorrect answers to the correct and recommended.
Notifications are done via web-push notifications and in-application notifications. In-application notifications are done using a basic Laravel package that does (almost) a great job.
A lot of the food has the same ingredients, so having a lot of ingredients duplicates does not make any sense.
The same situation is with the paid and free additions.
The solution is to make link tables. So, e.g. multiple dishes can link to the same ingredient, without duplication.
Food, paid/free addition, user, restaurant, admin tables are all soft-deleted, which means that the data will remain in the database, just it won't be shown anywhere like it was deleted. Data remains for analytics, marketing purposes...
The business model at its core is the same as the Grubhub model. 10% of the order price. A business model based on the value, the best type of business model (subjective).
App profits could be extended by bringing in delivery extension, so the independent delivery man/woman can work and make some cash.
The web application is made for the small cities and the places around the city. Most of the big food delivery companies include the only city and that is the place where this application differs from the others.
The application is a nice startup for the focus on the first city. The structure is easily extendible for multiple cities.
The application was translated from Bosnian into English in a rush. There are some English mistakes.
This is also my second big project, which I did for the 6 months and 21 days, with only 3 days rest. #hustleCulture 🤣
What I learned:
Laravel - in and out
Vue - a lot of it (community, thank you for this gift)
Webpack - headache is the correct translation
MVC - the structure of in-development independent separate parts (if I have to describe it using my words)
SCSS - nice addition to the basic CSS
Real problem of software development: maintaining the same speed of development in a big, robust system where it's so easy to lose the pace; so the only way to go is quality, clean code, implementing defined principles
Redis - fascinating pub-sub; fascinating speed; and the hard thing, not coupled to the Redis strictly, but often related to it - cache invalidation... Nice place to look for logical creativity.
Simple is better
Better spend more time at the planning the database structure than to support bad design weeks/months later
ORM is the nice, fast way to go (didn't have touch with the systems where performance is the real issue, but I assume then the ORM is not the best option)
Ubuntu is easy, understanding the permission system in the multi-user operating system is one of the key concepts to the security (next stage for me is cloud). The scaling problem for the VPS-based solutions is easy to notice, so the huge arise in the cloud makes sense.
Using packages is a messy thing
GraphQL is a way to go in the big APIs, REST will waste so much because of its inflexibility
Developing detailed front end schemes before even deciding which technologies to use on the back end is the way to prevent a lot of the regrets
Writing important code without testing is a crime
PHP is nice and evolving, but at the enterprise always Java/C# is gonna be used
Thank you for reading. ☄️