A few weeks ago, I came across a developer sharing one of his side projects on GitHub: a Trello clone. Built with React, Redux, Express, and MongoDB, the project seemed to have plenty of scope for working on fullstack JS skills.
I asked the developer, Moustapha Diouf, if he’d be interested in writing about his process for choosing, designing, and building the project and happily, he agreed. I hope you’ll find it as interesting as I did, and that it inspires you to work on ambitious projects of your own!
Nilson Jacques, Editor
Before moving on to the approach, I’ll take some time to discuss how:
Keep in mind that since the entire project is on GitHub (madClones), I’ll focus on design and architecture rather than actual code. You can check out a live demo of the current code: you can log in with the credentials Test/Test.
I started by taking a couple of hours a day to think about my goals and what I wanted to achieve by building an app. A to-do list was out of the question, because it was not complex enough. I wanted to dedicate myself to at least 4 months of serious work (it’s been 8 months now). After a week of thinking, I came up with the idea to clone applications that I like to use on a daily basis. That is how the Trello clone became a side project.
In summary, I wanted to:
I wanted to build a Node.js server with Express and use a Mongo database. Every view needed to be represented by a document so that one request could get all the necessary data. The main battle was for the front-end tech choice because I was hesitating a lot between Angular and React.
I am very picky when it comes to choosing a framework because only testability, debuggability and scalability really matter to me. Unfortunately, discovering if a framework is scalable only comes with practice and experience.
I started with two proof-of-concepts (POCs): one in Angular 2 and another one in React. Whether you consider one as a library and the other one as a framework doesn’t matter, the end goal is the same: build an app. It’s not a matter of what they are, but what they do. I had a huge preference for React, so I decided to move forward with it.
I start by creating a main folder for the app named TrelloClone. Then I create a server folder that will contain my Express app. For the React app, I bootstrap it with Create React App.
I use the structure below on the client and on the server so that I do not get lost between apps. Having folders with the same responsibility helps me get what I am looking for faster:
I use create-react-app since it automates a lot of configuration out of the box. “Everything is preconfigured and hidden so that you can focus on code”, says the repo.
Here is how I structure the app:
Here is how I structure the app with a folder per domain represented by:
If I have a lot of business logic, I will add a service file. I do not try to predict anything, I just adapt to my app’s evolution.
When choosing dependencies I am only concerned by what I will gain by adding them: if it doesn’t add much value, then I skip. Starting with a POC is usually safe because it helps you “fail fast”.
If you work in an agile development you might know the process and you might also dislike it. The point here is that the faster you fail, the faster you iterate and the faster you produce something that works in a predictable way. It is a loop between feedback and failure until success.
Here is a list of dependencies that I always install on any React app:
Some people might tell you that you do not always need redux. I think any descent app is meant to grow and scale. Plus tools you get from using redux change your development experience.
Here is a list of dependencies that I always install on any Express app:
I start by picking a screen that I want to develop and list down all the features the user has access to. I pick one and start the implementation.
After a screen and/or feature is developed, I take some time to reflect on the added code and refactor if needed.
Let’s illustrate everything that I said above with an example. I develop all of my screens and features by considering the front-end and the back-end as two separate entities. I always start with the front-end, because it helps me know exactly what needs to be displayed. It is then very easy to head to the server and implement the database model and add the business logic.
First, I write down a feature description and add a bunch of scenarios to it. Here’s an example for the purpose of the article:
Feature: In the home view, I should see my name in the header and a list of my boards. Scenario: I can see my name in the header Given I am on the home Then I should see my user name in the header
With this basic scenario in mind, let’s look at how I’d work on the home view.
Using the Component-Driven Development (CDD) methodology, combined with BDD, helps split views into small components, making sure they are decoupled and reusable.
First of all, I build a static page with mocked data written in plain text and I style the page with CSS.
Second, I test that:
Finally, I create a Header User component and container and refactor the data that I mocked earlier in the Redux module initial state.
Since I am using the ducks structure, I can focus on one view at a time. If I notice that two views share the same data I can lift up my state and create a higher module that holds all that data. The final Redux state of the app consists of all the data I mocked.
Once all my scenarios pass, I look at my component and refactor it if I notice that it is very similar to another component that I already created. I can either start by refactoring the old component before adding the new one or I can just add the new component so that I can fail fast and think about a more elegant solution later.
I did not waste my time guessing or thinking about what the user needed to see. I can just build my view then decide on what data needs to be displayed. It is easier to react to the design and think while building rather than trying to think in advance about what needs to be displayed. In my experience, it sometimes adds a lot of overhead and unnecessary meetings. As long as you keep reusability in mind you will be fine.
The Redux store model that I come up with is crucial, because I use it to design my database and then code my business logic. Due to the work done on the view, I know the homepage needs to fetch the user’s name and boards. I noticed that I have personal boards and organization boards which means that I can separate these two entities and have two different schemas. The main goal is to work with normalized data and have all the heavy lifting done by the server so that I do not have to think about it.
CRUD (create, read, update, delete) is the basic set of operations any app needs, but I do not blindly add them to all of my routes. Right now, what I need to do is fetch data, so I just implement read. I can then write a Mongo query that adds a user to my database later.
I hope you enjoyed my approach to building full-stack applications. My main advice is never be afraid to do big refactors. I can’t count the number of times I changed my app file structure just because I knew it would not be scalable: details like the folder name, the folder depth, the way they are grouped by feature always make the difference.
I am never afraid to make mistakes because they help me learn: the faster I fail, the faster I learn, the faster I grow. If I make 100 mistakes and I learn from them, then I know 100 different ways of avoiding those errors again.
When I notice something that I don’t like, I fix it right away or in the next few days. Since I test my code, I can quickly tell whether or not I am breaking functionality that works.
Einstein said that “once you stop learning you start dying” and I believe that once you stop making mistakes you stop learning. Fail, learn and keep living.
I keep working on my project because it is always a work in progress. Just like a real app, it is in continuous change. My plans are to:
I have a very strict routine:
I don’t spend all of my free time working on these. During weekdays, 1-2 hours per day is rewarding enough. The weekend, though I do not restrict myself. As long as I have time, I’ll work on the project: I could be writing code, experimenting with a tool or just reading documentation.
Coding is an art and a craft and I take pride in writing the least code possible that works, while keeping it performant and elegant.