📜 ⬆️ ⬇️

Web application architecture. Stack Spring MVC + AngularJs

Hello, Habr.

Below you will find a translation dedicated to the development of web applications. The stack described by the author demonstrates an interesting possibility of combining Java and JavaScript, and also allows you to take a fresh look at creating one-page web applications.

In this case, we will ask if you want to see on the shelf the translation of the following books on Spring and AngularJS
')



The technologies Spring MVC and AngularJs together form a truly productive and attractive stack for developing web applications, especially those that require intensive work with forms.

This article will discuss how to build just such an application. We will compare this approach with other available options. A full-featured secure web application written using Spring MVC / AngularJs is located in this repository on GitHub.

We will consider the following questions:



Architecture of the one-page web application Spring MVC + Angular

Applications for the corporate environment, involving intensive work with forms, are best done in the form of single-page web applications. The main idea that makes them stand out against the background of more traditional server architectures is to create a server as a set of stateless, reusable REST services. In the context of MVC, it is important to remove the controller from the machine interface and transfer it to the browser:



The client supports the MVC pattern and contains all the presentation logic, which is divided into the presentation level, the controller level and the client services level. When the application is launched, the client and server will only exchange JSON data.

How is the machine interface implemented?

The machine interface of a corporate application with a client part is logical and convenient to write as a REST API. The same technology can be used to provide web services to third-party applications. Often, in such cases, there is no need for a separate stack of SOAP web services.

From the point of view of DDD, the domain model remains on the machine interface, in the same place where the level of services and the level of persistence are. Only DTOs are transmitted over the network, but not the domain model.

How to structure the client part of a web application using Angular

The client part should line up around the model related to the view (and not the subject area). Here only the presentation logic should be processed, but not the business logic. In the client part, there are three levels:

Presentation level

The presentation layer consists of HTML templates, CSS tables, and Angular directives, corresponding to the various components of the user interface. Here is an example of a simple presentation for a login form :

<form ng-submit="onLogin()" name="form" novalidate="" ng-controller="LoginCtrl"> <fieldset> <legend>Log In</legend> <div class="form-field"> <input type="text" ng-model="vm.username" name="username" required="" ng-minlength="6"> <div class="form-field"> <input type="password" ng-model="vm.password" name="password" required="" ng-minlength="6" pattern="(?=.*\d)(?=.*[az])(?=.*[AZ]).{6,}"> </div></div></fieldset> <button type="submit">Log In</button> <a href="/resources/public/new-user.html">New user?</a> </form> 


Control level (controller)

The control level consists of Angular controllers, sticking data together, retrieved respectively from the machine interface and from the view. The controller initializes the view model and determines how the view should respond to model changes and vice versa:

 angular.module('loginApp', ['common', 'editableTableWidgets']) .controller('LoginCtrl', function ($scope, LoginService) { $scope.onLogin = function () { console.log('Attempting login with username ' + $scope.vm.username + ' and password ' + $scope.vm.password); if ($scope.form.$invalid) { return; } LoginService.login($scope.vm.userName, $scope.vm.password); }; }); 


One of the main tasks of the controller is to perform validation in the client part. All such client validations are provided only for the convenience of the user - for example, with their help, it is convenient to immediately inform the user that the field is required.
All acts of client validation must be repeated on the machine interface (at the service level) for security reasons, since client validation is easy to bypass.

Client Services Level

In Angular controllers, you can embed a set of Angular services that can interact with the machine interface:

 angular.module('frontendServices', []) .service('UserService', ['$http','$q', function($http, $q) { return { getUserInfo: function() { var deferred = $q.defer(); $http.get('/user') .then(function (response) { if (response.status == 200) { deferred.resolve(response.data); } else { deferred.reject('Error retrieving user info'); } }); return deferred.promise; } 


Let's look at what other libraries we need to start the work of the client part.

Which JavaScript / CSS libraries should complement Angular?

Angular already provides much of the functionality needed to create the client side of your application. Here are some interesting additions for Angular:



Armed with these two libraries and Angular, you can build almost any application with forms, practically nothing is needed anymore. Depending on the specifics of your project, some other libraries may be useful:



How to build a machine interface REST API using Spring MVC

This machine interface contains the usual levels:



Currently, the best Spring MVC configuration involves only using the Java configuration. Even web.xml
web.xml
already, in general, is not required. See here an example of a fully configured application that uses only Java.

Service levels and persistence are created using the normal DDD model, so let's pay attention to the routing level.

Routing level

The same Spring MVC annotations that are used to create a JSP / Thymeleaf application can also be used in developing the REST API.

The big difference is that the controller's methods do not return a String object that would determine which view template to display. Instead, the @ ResponseBody annotation is applied, indicating that the return value of the controller method should be directly displayed and become the body of the response:

 @ResponseBody @ResponseStatus(HttpStatus.OK) @RequestMapping(method = RequestMethod.GET) public UserInfoDTO getUserInfo(Principal principal) { User user = userService.findUserByUsername(principal.getName()); Long todaysCalories = userService.findTodaysCaloriesForUser(principal.getName()); return user != null ? new UserInfoDTO(user.getUsername(), user.getMaxCaloriesPerDay(), todaysCalories) : null; } 


If all class methods are to be annotated @ResponseBody
@ResponseBody
it is better to provide the entire class with the @RestController
annotation @RestController
.

If you add the Jackson JSON library, the return value of the method will be converted directly to JSON without any further configuration. In addition, this value can be converted to XML or other formats, depending on the value of the HTTP Accept
header. Accept
specified by the client.

This shows a pair of controllers that have error handling configured.

How to protect the REST API with Spring Security

The REST API can be secured using the Spring Security Java configuration. In this case, it is advisable to use the login form with HTTP Basic authentication as a backup option, as well as connect the protection from CSRF and the ability to hard to specify that all methods of the machine interface can only be accessed via HTTPS.

Thus, the machine will offer the user a login form, and after successful login, will assign the session cookie to browser clients, but will also work with other clients, supporting rollback to regular HTTP in cases where credentials are transmitted using the HTTP Authorization header.

In accordance with the recommendations of OWASP, REST services can be programmed with minimal state preservation (all server state information is limited to the session cookie used for authentication). This is done so as not to send credentials over the network with each request.

Here is an example of configuring REST API security:

 http .authorizeRequests() .antMatchers("/resources/public/**").permitAll() .anyRequest().authenticated() .and() .formLogin() .defaultSuccessUrl("/resources/calories-tracker.html") .loginProcessingUrl("/authenticate") .loginPage("/resources/public/login.html") .and() .httpBasic() .and() .logout() .logoutUrl("/logout"); if ("true".equals(System.getProperty("httpsOnly"))) { LOGGER.info("launching the application in HTTPS-only mode"); http.requiresChannel().anyRequest().requiresSecure(); } 


This configuration takes into account authentication only in the security context, and the authorization strategy is chosen depending on the security requirements of the API. If you need fine control over authorization, check out the Spring Security ACLs access control lists and see if they are appropriate for your task.

Now let's compare this way of creating web applications with other common approaches.

Comparing Spring MVC / Angular stack with other common variants

This way of using JavaScript in the client side and Java - working with a database simplifies the workflow and increases its productivity.

When the machine interface is already running, no special tools or plugins are required to disperse the hot deployment in the client side to full capacity: just publish resources on the server using the IDE (for example, press Ctrl + F10 in IntelliJ) and refresh the page in the browser.

Machine interface classes can still be reloaded using JRebel , but in the client part, nothing needs to be done separately. In principle, you can build the entire client part, simulating the machine interface with, say, json-server . In this case, various specialists will be able to develop in parallel the client part and the machine interface, if required.

Increased productivity or full-blown development?

In my experience, the ability to directly edit HTML / CSS without any levels of indirection (see, for example, the general comparison of Angular with GWT and JSF ) helps to reduce mental load and not complicate the work. The development cycle “edit-save-update” is very fast and reliable, it allows you to work much more productively.

The greatest gains in productivity are achieved in cases where the same developers write both the client side in JavaScript and the machine interface in Java, since for the implementation of most of the features, simultaneous changes are usually required both there and there.

The potential disadvantage of this approach is this: these developers need to know HTML, CSS, and JavaScript, but in recent years such competence is becoming more common.

My experience suggests that full-stack development allows you to implement the most difficult cases in the client side for a fraction of the time it takes to create a full-scale Java solution (days, not weeks), and this increase in productivity definitely justifies additional training.

findings

The combination of Spring MVC and Angular allows you to really take a fresh look at the development of web applications related to intensive form filling. This approach increases productivity so much that you should definitely look at it.

The absence of a binding to the server state between requests (except for authentication with a cookie), by definition, relieves us of a whole class of bugs.

Additionally, I suggest to get acquainted with this application on github .

Source: https://habr.com/ru/post/269217/


All Articles