On the side-project that I’ve mentioned before, I’m making a priority of 2 things: (1) use the least amount of libraries and frameworks, and (2) organize the code in a way that reflects the logical structure of the app.
Things that I like
On the backend I use Express.js for HTTP, with a very thin adapter that plugs it into the app. It’s not a RESTful API, it’s more like an RPC: The backend is a single endpoint listening for POSTs on
/ and receives 2 pieces of data: the scenario name and the scenario DTO which is the input for the scenario. Having such a small API surface keeps the app logic very neatly separated from the technicalities of HTTP, and as a result allows me to run the app from the command line if I later want to run it as a worker, and also makes it simpler to unit test because it’s not entangled with HTTP.
When thinking about the code organization, I’m trying to find a way that would easily answer the question: “What does this app do?”. One way that I have found to make sense is to use the concept of scenarios (also known as use-cases, user stories, and jobs to be done), so they reflect the activities that the app users will want to do. I have put that code in the
shared module because I will need that knowledge on both, frontend and backend sides. For every scenario, there will be a scenario handler on the backend, and a page on the frontend.
The business details like model validation will also live in the
shared module because I’m using them too on both sides: in the browser to validate the forms before submitting them, and on the backend to ensure the data integrity.
I haven’t quite settled on how to do write the validation code. For now, it’s robust but not as elegant as I thin it could be. I’m still keeping an eye on opportunities (railway seems interesting, but I haven’t tried it yet on this project).
For persistence, I decided to try to get away without an ORM for now: I’m using a very thin layer that gives me a MySQL connection which allows me to do 2 things: (1) run prepared SQL statements, and (2) give me back the rows. Will review this decision later.
Things to improve
At this time I have two levels of code sharing:
between the frontend and the backend — where I have the scenarios and the models;
between pages on the frontend — where I have components that are shared across pages.
The sharing pages is not perfect yet in terms of bundling: at this point, I bundle everything needed for a page in one bundle, which works, but the drawback is that I have the same shared code in multiple bundles which is (1) more bytes sent to the user, and (2) also takes longer to build during development. So there is an improvement opportunity there too.
One other area where I want to improve is the bundling: page bundles end up containing a considerable amount of shared code that they don’t actually use. So I’ll need a “tree-shaking” mechanism, preferably without bringing in an entire build pipeline for that.
Generally speaking, I’m intentionally making an effort to balance the tech work with the user-feature work, which means that I will not do work on improving the purely technical aspects at the expense of user features, because otherwise I would end up with a neat but useless system.