This article is an excerpt from a new Zine about JavaScript Application Architecture. Would you like to read more about this topic? Post it in the comments!
There are a lot of different approaches to Software Architecture. Choosing one depends on library support, your understanding of it, and if it helps to implement the features effectively.
Often, frameworks already come with a preferred approach to this. Rails defaults to MVC (Model View Controller), WPF on Windows uses MVVM (Model View ViewModel), and React has a preference for Flux.
All these different acronyms stand for a specific Design Pattern.
A design pattern is like a recipe that explains, “If you want to organize your Frontend UI Code, here’s an approach to do it.” The exciting thing about this is that it’s language-agnostic. MVC can be implemented in all sorts of different programming languages, as well as Flux or MVVM.
All of these different patterns have one thing in common. In one way or another, patterns help to separate code into different responsibilities. There is code that’s directly responsible for UI Elements (Click Handlers, Code that manipulates HTML and CSS) from business logic (Validations, Calculations, and other Algorithms) and Data Access (Database access, API Calls, File I/O).
Now, why would I separate all these concerns? Here’s an example.
I once worked on a Windows application that helped teammates to create duty rosters for their teams. They used it to manage shifts and determine monthly worked hours.
The original author knew enough to build a basic version of this application, even though their main job was to work in Energy Trading. I had just joined the team, and while we were waiting for approval on a new project, I put in some effort on this duty roster application. The application worked, but it mostly consisted of Spaghetti Code.
To give you an impression: The main user interface consisted of a large Table View that resembled the team’s duty roster. Each cell held a shift. Now, over a month, a team member could do different shifts on different days, and that’d determine how many extra hours they’d get credited.
Now, the rules to determine credit were directly tied to the UI Elements. Whenever the UI changed, because we had to introduce a new shift or rearrange the Table View, it also directly affected our business logic (the code that calculated the worked hours). This circumstance causes twice the work for a change: changing the UI Elements and also fixing bugs in business logic.
The first few weeks were quite frustrating because of this. Eventually, I decided to refactor (change the code but not its behavior) the code so that there’s a clear separation between UI and business logic. Refactoring meant that we could even introduce Unit Tests for the calculation algorithm and allow us to make future changes more confidently.
How does such a refactor look?
Before, UI Elements “were holding the truth.” Holding the truth meant that once the application had fetched all data from the database, it directly created UI elements from it and assigned the values to them.
Before any calculation, I needed to pull the data out of the UI elements first, perform typecasts, and then finally perform my calculations.
After the refactoring, the code looked like this: I fetched data from the database, render the UI elements from it, but then keep that data around. Whenever I needed to re-render the UI elements, I could do it directly from the data I had saved up. Additionally, to perform my calculations, I could pass them directly to the algorithm without unnecessary and tedious conversions.
This change paid off significantly when stakeholders requested a new view that showed existing data in a different format. For this view, I didn’t have to perform additional database queries or pull data out of UI Elements. I could directly render a new view that I had stored behind the UI.