The four layers of single page applications — by Alberto V
Every successful project needs a clear architecture, which is understood by all team members.
Imagine you’re new to the team. The technical leader presents the proposed architecture for the new application coming up on the roadmap:
The four layers of single page applications (detailed)
He talks about the requirements:
Our app will display a list of articles. As a user, I will be able to create, delete and like articles.
And then he asks you to do it!
I’ve chosen Create React App and Flow for type checking. For brevity, the application has no styling.
As a prerequisite, let’s talk about the declarative nature of modern frameworks, touching on the concept of state.
React, Angular, Vue are declarative, encouraging us to use elements of functional programming.
Have you ever seen a flip book?
A flip book or flick book is a book with a series of pictures that vary gradually from one page to the next, so that when the pages are turned rapidly, the pictures appear to animate … [1]
Now let’s check a part of React’s definition:
Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes … [2]
And a part of Angular’s:
Build features quickly with simple, declarative templates. Extend the template language with your own components … [3]
Sounds familiar?
Frameworks help us build apps consisting of views. Views are representations of state. But what is the state?
The state represents every piece of data that changes in an application.
You visit an URL, that’s state, make an Ajax call to retrieve a list of movies, that’s state again, you persist info to local storage, ditto, state.
The state will consist of immutable objects.
Immutable architecture has many benefits, one being at the view level.
Here is a quote from React’s guide to optimizing performance:
Immutability makes tracking changes cheap. A change will always result in a new object so we only need to check if the reference to the object has changed. [4]
The domain describes the state and holds the business logic. It represents the core of our application and should be agnostic to the view layer. Angular, React, Vue, it shouldn’t matter, we should be able to use our domain regardless of the framework we choose.
The domain layer
Because we are dealing with immutable architecture, our domain layer will consist of entities and domain services.
Controversial in OOP, especially in large-scale applications, the anemic domain model is perfectly acceptable when working with immutable data.
For me, this course by Vladimir Khorikov was eye-opening.
Having to display a list of articles, the first thing we’ll model is the Article entity.
All future objects of type Article are meant to be immutable. Flow can enforce immutability by making every property read-only (see the plus sign before each prop).
Now let’s create the articleService using the factory function pattern.
Check out this video by @mpjme for a great explanation**.**
Since we need only one articleService in our application, we will export it as a singleton.
The createArticle method will allow us to create frozen objects of type Article. Each new article will have a unique autogenerated id and zero likes, letting us supply only the author and title.
The
**Object.freeze()**
method freezes an object: that is, prevents new properties from being added to it. [5]
The createArticle method returns a “maybe” Article type.
Maybe types enforce you to check if an Article object exists before operating on it.
If any of the fields necessary to create an article fail validation, the createArticle method returns null. Some may argue that it’s better to throw a user-defined exception. If we enforce this and the upper layers do not implement catch blocks, the program will terminate at runtime.
The updateLikes method will help us update the number of likes from an existing article, by returning a copy of it with the new count.
Finally, the isTitleValid and isAuthorValid methods prevent the createArticle from working with corrupt data.
Validations are very important in keeping our data consistent, especially at the domain level. We can compose our Validators service out of pure functions.
Please take these validations with a grain of salt, just for demo purposes.
In JavaScript, checking if an object is, in fact, an object is not that easy. :)
We now have our domain layer setup!
The nice part is that we can use our code right now, agnostic of a framework.
Let’s see how we can use the articleService to create an article about one of my favorite books and update its number of likes.
The data which results from creating and updating articles represents our application’s state.
We need a place to hold that data, the store being the perfect candidate for the job.
The store layer
The state can easily be modeled by an array of articles.
The ArticleStoreFactory implements the publish-subscribe pattern and exports the articleStore as a singleton.
The store holds the articles and performs the add, remove and update immutable operations on them.
Keep in mind that the store only operates on articles. Only the articleService, can create or update them.
Interested parties can subscribe and unsubscribe to the articleStore.
The articleStore keeps a list in memory of all subscribers and notifies them of each change.
Our store implementation makes sense for demo purposes, allowing us to understand the concepts behind it. In real life, I recommend using a state management system like Redux, ngrx, MobX or at least observable data services.
Ok, right now we have the domain and store layers setup.
Let’s create two articles and two subscribers to the store and observe how the subscribers get notified of changes.
This layer is useful for doing all kinds of operations which are adjacent to the state flow like Ajax calls to retrieve data from the server or state projections.
The application services layer
For whatever reason, a designer comes and demands all author names to be uppercase.
We know this request is kind of silly and we don’t want to pollute our model with it.
We create the ArticleUiService to handle this feature. The service will take a piece of state, the author’s name, and project it, returning the uppercase version of it to the caller.
Let’s see a demo on how to consume this service!
Right now we have a fully working application, agnostic of any framework, ready to be put to life by React.
The view layer is composed of presentational and container components.
Presentational components are concerned with how things look while container components are concerned with how things work. For a detailed explanation check out Dan Abramov’s article.
The view layer
Let’s build the App component, consisting of the ArticleFormContainer and ArticleListContainer.
Now let’s create the ArticleFormContainer. React, Angular, it does not matter, forms are complicated.
Check out the Ramda library and how it’s methods enhance the declarative nature of our code.
The form takes user input and passes it to the articleService. The service creates an Article from that input and adds it to the ArticleStore for interested components to consume it. All this logic resides primarily in the submitForm method.
Notice that the ArticleFormContainer returns the actual form which the user sees, the presentational ArticleFormComponent. This component displays the data passed by the container and emits events like changeArticleTitle, changeArticleAuthor, and submitForm.
Now that we have a form to create articles, it’s time to list them. ArticleListContainer subscribes to the ArticleStore, gets all the articles and displays the ArticleListComponent.
The ArticleListComponent is a presentational component. It receives the articles through props and renders ArticleContainer components.
The ArticleContainer passes the article data to the presentational ArticleComponent. It also implements the likeArticle and removeArticle methods.
The likeArticle method updates the number of likes, by replacing the existing article inside the store with an updated copy.
The removeArticle method deletes the article from the store.
The ArticleContainer passes the article data to the ArticleComponent which displays it. It also informs the container component when the like or delete buttons are clicked, by executing the appropriate callbacks.
Remember the crazy request that the author name should be uppercase?
The ArticleComponent uses the ArticleUiService from the application layer to project a piece of state from its original value (string with no rule for uppercase) to the desired one, uppercase string.
We now have a fully functional React app and a robust, clear defined architecture. Anyone who joins our team can read this article and feel comfortable to continue our work. :)
You can check out the finished app here and the GitHub repository here.
If you liked this guide, please clap for it. If you want to help me improve it, I am interested in your comment. @danielDughy
[1] https://en.wikipedia.org/wiki/Flip_book[2] https://reactjs.org[3] https://angular.io[4]https://reactjs.org/docs/optimizing-performance.html[5] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze