Building a SaaS with TypeScript, ExpressJS, and SOLID principles: the good the bad and the ugly

#14 — Learning how to create a clean project with TypeScript following the SOLID principles

Sandoche ADITTANE
Learning Lab
10 min readApr 28, 2020

--

It has been 3 years where every month (or two) I learn about one new topic. It’s what I call the Learning Lab challenge 🚀. This time I took a bit longer to learn about both the TypeScript language and the SOLID architecture.

As a developer, I constantly need to be updated with the latest technology, principles, and paradigms. I don’t necessarily need to master them but at least to know what they are so that I am able to jump easier on projects.

When I start a backend or full-stack project I usually go for Symfony (a well-known PHP Framework) when it’s a big or middle-sized project, or ExpressJS (Node.JS) when it’s a smaller one. Symfony is pretty amazing, everything comes out of the box, building an API is almost magic, is more putting the right settings and annotation than “real programming”, the framework is very structured and forces you to follow it. On the other hand ExpressJS is very handy and easy to start with, but regarding structure, you are on your own. YOU and only YOU can make the code well structure and clean 🤔.

After talking with other developers, I found two ways to make an ExpressJS project more structured:

  1. By using a SOLID architecture
  2. By using TypeScript

That’s why I decided to learn SOLID Architecture and TypeScript together🙌.

In this article, I will give a brief overview of TypeScript and the SOLID principles, how I built my SaaS with them, and finally, take a look back at this experience to give my opinion.

How to learn the SOLID principles and TypeScript?

Once again, I followed the Learning Lab methodology.

  1. Finding a mentor
    I didn’t look for a mentor this time. When it comes to learning any coding related skill, there are plenty of tutorials and articles online
  2. Defining the scope of the topic
    The scope of this topic is understanding the SOLID Architecture and how to use TypeScript (and create types of course)
  3. Choosing a learning resource
    I found a lot of interesting resources from different places, you can find them here: https://learn.uno/learning/typescript-solid/
  4. Defining a project
    I took one of the projects I had in my backlog of ideas: a SaaS that aggregates sales reports from Google Play and the App Store. I called this project Revenual 💵.

Why SOLID & TypeScript are good co-workers?

What is TypeScript about?

TypeScript is an open-source language created by Microsoft, it has the same syntax as JavaScript but it adds Types. Therefore you can have more robust code, and TypeScript transpiles to JavaScript, if of course there is no type errors! This is something that you cannot have in JavaScript and what makes it so amazing.

TypeScript makes JavaScript more object-oriented, with inheritance, interfaces, abstract classes. This makes it possible to follow the SOLID Principles because a lot of the principles rely on interfaces!

What is SOLID Architecture?

SOLID Architecture or SOLID Principles are a set of principles to make your code more re-usable, easy to maintain, and less coupled.

SOLID is an acronym. Here is what it stands for.

Source: https://devopedia.org/solid-design-principles

If you want to know more about the SOLID Principles, read the following article.

How to build a SOLID SaaS from 0 to 1

Here is the process I followed to build my SaaS!

1. Find a problem to solve 🤔

I previously built some mobile apps with NativeScript VueJS and Flutter with In-App Purchases. The problem is that Google Play doesn’t give you an easy to digest report and, also, I had to go back and forth between iOS and Google Play platforms to see my sales per product. Moreover, because I have my own company I need invoices for my bookkeeping. I really wanted to automate this process.

2. Check what are the existing alternatives

Before answering your problem, check if there isn’t anyone who has answered it already, how they have answered it, and their pricing strategy, to be able to be different from them! In my case, in my initial research, I didn’t find any direct competitor. After building the core of the app a friend of mine found a competitor. But the competitor is targeting more subscriptions and not any sort of sales!

3. Check the feasibility manually

Before starting Revenual, I built a proof of concept just to check if the project is technically feasible. Basically I needed to know if there was a way to download sales data from both Google Play and the App Store. I found a library called “apple-reporter” to do it easily from the App Store, and I managed with Google Play after a bit of struggle. I built two small scripts in JS to prove it and it worked 🙌!!

4. Define the UX Flow

I based the UX Flow on my user’s need. I just took a paper and a pencil and draw it. I thought about how I would feel as a user, and update it until it would satisfy me.

5. Define the architecture of the information

Once the UX is more or less defined, I define for each of the pages the EXACT content that needs to be written (with the exact text)! This will shape the UI but also helps to define the data model of the app.

This is an example of the content I wanted to display in the sales reports page.

I talk more about the information architecture in this article.

6. Draw the wireframes

Once you know what to put on each page you can draw the UI of each page. You can find a part of the UI/UX of Revenual. As you can see it’s both UI and UX to go faster. Also, I used the content defined at the previous step!

Here is one parts of my UI/UX. Pretty ugly right ? 😅 The most important is to be able to read it yourself!

7. Define the pricing model

At some point, you need to define a pricing model. In my case, I started with one model based on the number of Google Play/App Store accounts connected, but after getting some feedback I changed it, to something related to the number of products sold that scales with the user. Try to get feedback as soon as you can. For both the pricing, the UI, the idea, anything. Don’t be shy, share your idea, no one will steal it.

8. Define your data model

Based on all your previous info you can now build your data model, you know that you have:

  • users
  • subscription
  • accounts
  • reports

And of course for each data model, define how they relate to each other and what are their attributes so you can later code it!

9. Chose your stack

Now we have the data model and the information of architecture, we need to choose a stack. What I usually do is to take something I am the most familiar with that fits the need of the project. The app should:

  • Connect to accounts
  • Download reports, unzip them and parse them
  • Parse reports
  • Save reports
  • Display reports
  • Charge the user

As you can see a lot of these steps are asynchronous. One way is to use a synchronous solution along with background jobs. The other way is to use an asynchronous language.

Since I also wanted to learn TypeScript, I went for Node.JS. I like to start with boilerplate and I found a TypeScript boilerplate made by Microsoft that included already authentication and was connected to MongoDB. So my choice was done.

Here is my stack 🧱

  • NodeJS
  • TypeScript
  • Express.JS
  • MongoDB
  • Stripe

10. Define the architecture

From the UX, and the data model defined before we can do a few things:

  1. Draw a sequence diagram
  2. Defines the routes of the website
  3. Split the project in services (user, accounts, reports, invoices, subscriptions)
  4. Define interfaces (here is where you can feel the power of the SOLID principles and TypeScript)
  5. Define where you need which design pattern

For the file structure and code architecture I recommend this article:

Also, I did two choices that are faster for me:

  • To have a monolithic architecture but independent enough to be able to split them into microservices in the future
  • To not build an API but to render pages (to avoid having to build a frontend app and a backend), but to be able to change it easily in the future

11. Write code 👨‍💻

Now everything is defined, I just wrote the code, tested on the fly with my data, fix the bugs, handled errors... I’ve also learned a lot from the following link, explaining all the best practices.

12. Write tests

I wrote some simple tests for the most critical part of the code only, such as parsing the data from Google Play, App Store, and the report generation.

13. Design the UI and implement it

As concern the UI, I was highly influenced by this book that is pretty amazing: https://refactoringui.com/book/

I used the twig templating engine to implement it, and I used SCSS and split the style into components to later switch easily to a full frontend web app.

14. Make users test

Last but not least, I tested with real users and fixed the bugs.

15. Release

Time to release now 🚀, on Product Hunt, Hacker News, and Reddit to start with!

The Good the Bad and the Ugly

Here is the good, the bad, and the ugly of my experience with TypeScript with the SOLID Principles. The overall experience is excellent but there are a few takeaways to avoid mistakes for the next SaaS I’ll build!

The good 👍

I really enjoyed TypeScript, here are the most remarkable reason and lessons from this experience:

  • Types — Types are amazing. Adding types does not make the coding experience slower, instead, they make you make fewer mistakes, and also it makes the code easier with the auto-complete that tells you exactly what type you need to put in your functions.
  • Linters — You cannot work on a TypeScript project without linter; they make your code consistent but also detects potential security leaks. It is sometimes annoying but mostly good for your code quality and consistency.
  • TypeScript Node Starter — This boilerplate was key. Because it adds some initial structure to your project but also includes the authentication and user management. That is one thing you don’t have to take care of! If you didn’t see it before, here it is: https://github.com/Microsoft/TypeScript-Node-Starter
  • Libraries — Node JS is very good in general because you have a lot of libraries on NPM, for every problem, there is a library to solve it. It is also very easy to create your own reusable modules and make the code more clear!
  • Interfaces — I usually don’t create Interfaces, but it’s a key to the SOLID principles. It was very fascinating to be using them. I used them to implement the connection of the different accounts (Google Play, App Store) and also to manage the logic of the data aggregation.
  • Dependency Inversion — Event though I am not sure I applied the Dependency Inversion as suggested by the SOLID principles, I used dependency injection which is key for automated testing. I don’t often write tests, but when I do I am very proud of it, and thanks to that I finally understand fully the purpose of dependency injection. Without dependency injection, testing is a lot more complicated. I used the library Awilix to manage the dependency injection.
  • PM2 — It is an amazing tool that I didn’t know before to manage the deployment of the app. It made everything very easy and will restart your server in case of a crash, which is very useful.
  • Full understanding — Last but not least, writing a website is NodeJS / ExpressJS forces you to understand everything that you do, unlike frameworks like Symfony that handles a lot of crazy things out of the box.

The Bad and the Ugly 👎

Now let’s talk about the bad side of NodeJS, Express, and TypeScript:

  • Discipline — To have a good code following SOLID principle, you need crazy self-discipline, you need to force yourself to do good, and to keep refactoring when you didn’t.
  • File Structure — The file structure is up to you, you decide everything, it’s good sometimes for small scale projects, but as soon as it gets big it can be chaotic.
  • State — NodeJS is stateful so you have to make it stateless by persisting any data.
  • Error management — When an error is not caught your server can just crash, so you need a tool like PM2 to ensure that it reboots, but also have a very well built error management system, which requires a lot of work and experience to make it perfect.

My key learning

I really liked TypeScript and the SOLID principles are very good. But if I had to redo Revenual, I would build it with Symfony mainly and will trigger jobs in NodeJS / TypeScript to handle all the data aggregation.

Check out the result 🙌

Here it is: https://revenual.com
Revenual — Aggregated sales reports for app makers

Sales report of Revenual

I hope you like it 😀.

If you liked this post, please click the clap 👏button below a few times to show your support! Also, feel free to comment and give any kind of feedback. Don’t forget to follow me!

Want to see more articles like this one? Support me on Patreon 🙌

--

--

Sandoche ADITTANE
Learning Lab

Hello, I’m Sandoche Adittane. I learn about one topic every month and write a post about it!