Serving The First Million Orders - Part 1
Zid started in 2016 as an e-commerce platform mainly geared to serve Instagram based merchants. The idea was to migrate these merchants from mostly WhatsApp run operations to a more organized process through a mobile app. Later, we realized that merchants of all sizes are in need of a platform and an ecosystem to transform them from traditional commerce to e-commerce.
In this post, I will talk about Zid's technology journey from the first order to the millionth. I will divide the story into four stages, serving the first 1K, 1K to 100K, 100K to 1M, and 1M and beyond.
Serving the first 1K orders: Open Cart Era
We choose OpenCart because we had experience with it, and it's one of the easier PHP-based systems to hack. The first problem we had to solve was that OpenCart was designed for single store deployments, so we had to alter many tables to add a store_id
field referring to store's record in the stores
table that we also added. Then, we had to add a web API layer to the mix so that we can communicate with the system from the mobile app even though the same application still rendered the catalog
(where customers shop the stores).
Many of the changes we made were either OpenCart plugins or extending OpenCart's classes. We tried to do proper OpenCart development as much as possible; we even used some open-source plugins.
However, OpenCart lacked ORM, proper routing, web APIs, and correctly-layered design pattern. A couple of months after releasing the service to the public, we quickly realized that it's time to move on, as OpenCart can no longer sustain our development velocity and the rapidly changing requirements. Additionally, we were running that system on a single VPS
by a generic hosting provider. That instance was running the application, the database, and hosting the media files. So that needed to be sorted out too.
1K to 100K: Laravel based Open Cart
At this stage, we decided to modernize the codebase by rewriting it. We basically threw away all the PHP code and started over. Notice that we only rewrote the PHP side, we were still using the same database schema for better or for worse. That made implementing the API layer easier while not having to rethink the logic all over again – keep in mind this was still a rapidly growing business with requirements changing every week.
Moreover, we decided to break our system into three components: the API, the catalog, and the dashboards (i.e., web, iOS, and Android). This meant we could efficiently write APIs that can be used by all clients without duplicating the logic. Though, that was not entirely the case due to our decision to use different namespaces for different client types. For example, management endpoints used by the web dashboard went under /managers
, and the browsing/purchasing endpoints went under /catalog
. This made the controllers shorter and more straightforward, and did not require us to check where the request is coming from. However, we had to duplicate some code between the endpoints. At that point, this API was not public and did not have proper versioning – that was done purely through coordination.
On the infrastructure side, we migrated to Heroku to host our application servers and AWS for our queues, object storage, and managed database hosting. Also, we started using Cloudflare for DoS protection and custom domain support for merchants.