MVC (Model, View and Controller) architecture has been in the industry for quite sometime. This pattern has served web applications well by decoupling their various components. The following diagram gives a high level idea of how interaction between different components work.
Controller is a broker between View and Model. The flow of data happens between model-controller and controller-view. Since view and model don’t have a direct communication hence they are unaware of each other. This allows us to change them independent of each other.
With the advent of complex frontend heavy applications (eg. gmail), this pattern found its way in javascript as well. The above diagram can be mapped to javascript applications as follows:
On front end, data flows from the view to server due to user interactions (for eg. button click). Similarly data flows back to the view from the server in case of server updates.
For applications which have thin front end (i.e. no business logic in JavaScript), the role of controller is that of a data binder between model and view. For instance,
//Send form data to server on click of button $('#clickMeBtn').click(function(){ form.submit();}) //Update view with data recieved from server $.get('/get/employee', function(employee){ $("#employeeName").text(employee.name); })
The above code does the basic functionality of relaying data back and forth between view and server.
However, for an application with good amount of user interaction adding such code can become repetitive and combursome. In such scenarios, we can leverage AngularJS’s two-way data binding. This allows us to declaratively bind the view with model thereby relieving controller of this duty. For such thin applications, custom controllers don’t play a significant role and are just a support function to AngularJS.
Hence, in a thin javascript application with considerable amount of user interaction we can have a bidirectional data flow directly between the view and server.
Things start becoming interesting and complex for applications which have thick front end. Such applications have a good amount of business logic on the client side as well. One case of adding business logic on client side is to make the application seem highly nifty and responsive. This leads to higher user engagement and retention. For eg. social networking sites like Facebook and Twitter have thick front ends as they want to give a user instant feedback and keep them engaged.
In such applications, controllers start to play a significant role. As a result, they come out of the shadow of AngularJS and start showing their influence on View and Model.
The above diagram shows that data can flow between view and model from either the controller or AngularJS. This kind of architecture gives us low confidence on how the view and model interact. We can never tell with surety if the view got updated logically or as a side effect by AngularJS.
In an application, view is a function of the data provided to it by the server. Mathematically speaking,
Mathematical functions are pure functions, which means that a given input maps to the same output all the time.
In the current architecture, we cannot say with a high degree of confidence that given the same data from server will our application show the same view. This is due the fact that some parts of our view change which are not in our control (or not easy to reason about).
In such thick frontend applications, we have to introduce the concept of unidirectional data flow.
Here, view talks to a component called Dispatcher through actions. Actions are generated through user-generated events like click or keypress. Dispatcher figures out what data this action needs to update and sends it to the Application Store for further processing. Application Store contains all the state information of the application. It can also communicate with the server for updates. Updates to the store are listened by the View which in turn updates itself.
Since the flow of data is unidirectional, there is only one way through which view can be updated. As a result this gives us a high degree of confidence that our view is a function of data and hence is deterministic.
The above architecture is as simple as it looks. We can find variations of this architecture in the form of flux, redux and elm.
The implementation of this architecture can become a bit more involved as compared to an out of the box bidirectional data flow architecture. However, this gives us more control over how data flows in our application.
Conclusion
For thin front end applications with minimum business logic in JavaScript, we can deliver quickly with bidirectional data flow in the form of two-way data binding provided by AngularJS. However, for thick frontend applications we should rely on unidirectional data flow to get more confidence regarding how the view will be displayed based on the state of the application.