/architecting-single-page-applications

Architecture and State Flow of Single Page Applications

Primary LanguageJavaScript

Architecting Single Page Applications Let’s architect a React application and understand state flow In this article, we will learn how to architect single page applications from the ground up. Application layersAs a prerequisite let’s talk about the declarative nature of modern frameworks, touching on the concept of state. Today’s frameworks are declarative React, Angular and most modern front-end frameworks 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]

Flip bookNow 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 Angulars: 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 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 should be immutable. Now let’s start architecting! Our application will display a list of articles. As a user, I will be able to create, delete and like articles. I’ve chosen Create React App and Flow for type checking. You can find the code supporting this article here. The domain layer The domain layer 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 layer regardless of the framework we choose. Domain layerBecause 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).

Article.jsNow let’s create the ArticleServiceFactory using factory functions. This factory will provide us with a brand new ArticleService each time we need it. 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. [4] The updateLikes method will help us update the number of likes from an existing article, by returning a copy of it with the new likes count. Both methods are curried, taking a validator argument and returning another function which deals with the actual business logic, creating articles and updating the number of likes.

ArticleServiceFactory.jsLet’s take a quick look at the ValidatorServiceFactory and the resulting ValidatorService.

ValidatorServiceFactory.jsPlease 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 any 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.

article-demo.jsThe store layer 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. Store layerThe state can easily be modeled by an array of articles.

ArticleState.jsIn our simple application, the ArticleStoreFactory implements the publish-subscribe pattern. The ArticleStoreFactory is a singleton and exports the article store. The store holds the articles and performs the add, remove and update immutable operations on them. The store solely operates with 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.

ArticleStoreFactory.jsIn complex applications, 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 se tup. Let’s create two articles and two subscribers to the store and observe how the subscribers get notified of changes.

store-demo.jsApplication services 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. For whatever reason, a designer comes and demands all article author names to be uppercase. We know this request is kind of silly and we don’t want to pollute our model with it. With the help of the ArticleUiServiceFactory, 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 the author’s name to the caller.

ArticleUiServiceFactory.jsLet’s see a demo on how to consume this service!

app-service-demo.jsThe view layer We reached the final layer of our application. 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 layerLet’s build the App component, consisting of the ArticleFormContainer and ArticleListContainer.

App.jsReact, Angular, it does not matter, forms are complicated and so is the ArticleFormContainer. The form gets user input and uses the ArticleService to create an Article and then it adds it to the ArticleStore for interested components to consume it (all this logic resides primarily in the submitForm method).

ArticleFormContainer.jsThe render method of the ArticleFormContainer, returns the ArticleFormComponent which is purely presentational. It receives formData to display and emits events like changeArticleTitle, changeArticleAuthor, and submitForm.

ArticleFormComponent.jsNow that we have a form to create articles, it’s time to list them. ArticleListContainer subscribes to the ArticleStore, gets all the Article entities and displays the ArticleListComponent.

ArticleListContainer.jsThe ArticleListComponent is a presentational component. It receives the articles through props and renders ArticleContainer components.

ArticleListComponent.jsThe ArticleContainer passes the article data to the presentational ArticleComponent. It also implements the likeArticle and removeArticle methods. The likeArticle method updates the article’s number of likes. It replaces the existing article inside the store with the updated one. The removeArticle method deletes the article from the store.

ArticleContainer.jsThe 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 likeArticle and deleteArticle callbacks.

ArticleComponent.jsThat’s it, we now have a fully functional React app! If you liked this article, maybe clap for it, comment or tweet me @danielDughy. [1] https://en.wikipedia.org/wiki/Flip_book [2] https://reactjs.org [3] https://angular.io [4] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze