Simpler architecture: specifics

Two weeks ago I wrote about the overall organization of the code on my side-project. I have since then added a couple more scenarios, and I want to get just a little bit into the specifics of it.

I think about the functionality of the system as user scenarios: specific workflows that users would go through to achieve something of value to them. For example the “Tutor Registration” scenario contains the code related to tutor registration (Yeah!!). For a web app, when it’s deployed, some of this functionality exists on the front-end, and some on the back-end, specifically:

  1. A page on the front-end.
  2. A scenario handler on the back-end.

I will get into more details on both of them, and some more.

1. A page on the front-end

This is where the UI for the interaction lives. For this specific scenario, it’s a separate page, but I guess that for some scenarios I’ll not need a whole new page, it can be a form, or a section, or a modal that can contain all the UI elements needed.

I have pages implemented as separate TypeScript modules, and the React code for each of them ends up bundled as an AMD module which gets loaded from the HTML template. The AMD is not a particularly modern module format, but I’m using it because the TypeScript compiler supports it out-of-the-box, and because It Works™.

If you’re asking where the CSS code goes, I’m glad you asked: it’s also bundled into the AMD module, together with the React code. I decided I’m going to use a CSS-in-JS approach with TypeStyle. This subject is worth a post on its own, so I’m not going to go into a lot of details today.

In short, all of the application UI HTML and CSS code ends up as a single JS file. The vendor modules come from NPM.

2. A scenario handler on the back-end

On the front-end, I collect all of the user input relevant to a scenario into a data transfer object (DTO), and submit it to a scenario handler on the backend.

I mentioned before that I have a thin integration layer — the scenario runner — between the ExpressJS app server, and my scenario handlers which only validates the scenario name and passes it the DTO, and sometimes the session object if necessary. It can be thought of as the routing layer in a more conventional web app.

The scenario handler is a function that receives its DTO, validates it, and invokes the necessary persistence code, and returns the response to the scenario runner which then passes it back to Express as the HTTP response.

3. The secret sauce

Yes, I know that I initially only mentioned 2 parts: the back-end and the front-end, but there is actually a third, a glue-of-a-sorts part, that ties the first two together: the shared/ module. It contains the pieces of code that are common between front-end and back-end: data types and logic that is, on one hand, reused between the two, but more importantly, it ties the two sides together such that if I add a field to the scenario DTO, the compiler can point out all the places that I need to update, on both sides. This means significantly less effort than I would have had on a pure JS codebase.

This glue, I think, is the secret sauce, one of the most valuable technical aspects of TypeScript that I wanted to make good use of, and which I’m glad I got in place.