About a month ago, Lectronz passed 4000 orders and is sailing towards 5000!
Lectronz continues its mission to be the best marketplace for creators of open hardware and DIY electronics.
Thanks to the continuous feedback we receive from the community of creators on Lectronz, we've been continuously improving our software. We fixed bugs, improved our UI, documented our APIs, and implemented many feature requests. We deploy an updated version of our platform almost every week.
This is possible because we decided to build lectronz.com with a lean tech stack.
How does it all work? Let's take a peak behind the curtain!
1. The lean tech stack
When Tindie, the first marketplace for makers, was founded ten years ago, it received $2.3M in venture capital funding. Times have changed since then, and there is no way Lectronz could get that type of money today.
This is not a bug. It's a feature.
From the onset, it meant that Lectronz needed to adopt a lean tech stack, which allows any developer to be operational on both frontend and backend concerns, with minimal moving parts and third-party dependencies. A lean tech stack will also enable changes to respond to evolving demands.
This drove us towards using Ruby on Rails, which offers a platform that enables small teams of developers to build full modern web applications in Ruby. The release of Rails 7 at the end of 2021 has marked the beginning of an exciting new era for Rails developers, making it the ideal lean tech stack for a web application today.
As a result, we were able to built Lectronz with the equivalent of a single developper working part-time.
Footnote: Ruby is fun. It's more fun than Python! ;-)
The following diagram summarizes the architecture of the Lectronz application.
Lectronz is hosted on a dedicated Linux server. While less scalable than pure cloud computing, this results in much more predictable costs.
A classic nginx + application + database foundation
The central part of this application is founded on a classic combination of three elements:
- A Nginx web server, acting as a reverse TLS/SSL proxy, forwards web requests to the application components for processing,
- The lectronz web application itself, built with Ruby On Rails and running on a Puma webserver,
- A PostgreSQL database backend.
Most web requests follow that path, except those that apply to images. On Lectronz, images are served by a minio server, which runs as an Amazon S3 object-store. This is reflected in the domain structure of the website, where images are served by images.lectronz.com instead of lectronz.com, which applies to all other web assets. The Nginx web server uses this distinction to forward requests to either the main application or the minio S3 object store.
Using minio lets us quickly move our object storage to a dedicated host with replicas, allowing us to scale up if needed.
Most requests to the Lectronz rails application are performed synchronously: the application receives a request, processes it, and responds immediately with the result. There are, however, a few tasks that need to be performed asynchronously because they could take longer than is acceptable or because they depend on external parties/API calls with unpredictable response times. Examples of such tasks include:
- Sending emails to users.
- Getting the latest daily USD/EUR exchange rates from the European Central Bank.
- Creating an order after verifying payment data from our payment processor.
These tasks are offloaded to a job manager Delayed Job which runs in a different process than the main rails application, thereby avoiding processing delays. Delayed Job also uses PostgreSQL as a persistence backend.
There are faster and more scalable job managers for Rails, most notably Sidekiq, which is based on Redis. However, these solutions introduce supplementary dependencies, and so far, the simplicity of using Delayed Job has largely been a benefit.
Many asynchronous jobs on Lectonz involve sending emails, such as order confirmations or fulfillment notifications. All emails are shipped by our self-hosted email SMTP server based on Postfix, with appropriate SPF, DKIM and DMARC records for domain authentication and spam reputation management.
Backups of the PostgreSQL database and the S3 object store are performed daily and stored on a distinct host through a backup server, which is inaccessible over the Internet. Should our dedicated host suffer an irreparable failure, we can rebuild our platform with a couple of hours worth of downtime.
While this is fine for now, we plan to improve our business continuity strategy in the near future.
We use IP addresses to guess the location of each visitor of lectronz.com to identify applicable taxes and shipping rates for products. The mapping between IP addresses and country codes is performed by an in-house application written in Go. This mapping is done once when the user first visits the website and then stored in a session cookie for subsequent lookups.
Each time a new version of Lectronz is ready for deployment, it goes through automated tests and security auditing, with static code analysis and dependency vulnerability checks. If all tests succeed, the new code is pulled down on the server from our GitHub repository and deployed, resulting in the re-launch of both the main rails application and the job manager. All this process is done automatically using Capistrano, a popular battle-tested tool for remote server deployment.
Lectronz interfaces with third party APIs, such as those provided by Stripe for payments, the European Central Bank for currency rates and the European VIES Registry for EU B2B tax verification purposes. These interactions are omitted on the diagram for the sake of clarity.
Building Lectronz has not been without challenges. I can mention at least two important ones here.
A complex price and tax engine
One of the strong features of Lectronz is the ability to manage prices and taxes on orders based on:
- the location of the customer,
- the location of the seller,
- the applicability of the IOSS scheme that allows non-EU sellers to import products in the EU hassle-free,
- the preferred display currency (USD or EUR),
- the status of the seller (individual versus business),
- European intra-community B2B VAT rules...
Any order on Lectronz can represent multiple combinations of these parameters. It took a long time to create a test suite capable of going through these parameters to avoid unexpected regressions between deployments.
Payments and edge cases
Lectronz does not store sensitive payment data: all payments on Lectronz are handled by Stripe.
While Stripe offers mostly great support and documentation, we've had our load of edge cases and puzzling issues:
- Payments that are confirmed more than once,
- Dealing with refund delays,
- Dealing with the intricacies of payment mechanisms (Apple Pay, Giro Pay, etc.)
- Customers that open multiple payment windows simultaneously,
We've learned a lot since our first days.
4. What's up next
Building the software behind lectronz.com has been full of challenges and fun.
Please keep sharing your feedback and ideas with us as we grow.
And stay tuned.
We will soon launch a pre-order system allowing creators to list a product while setting a minimum number of units to sell. Interested customers can pre-order the product, thereby demonstrating a demand. Once the minimum number of pre-orders is reached, sellers can order parts and inventory and transform these pre-orders into confirmed orders.