Web application architecture. Stack of Spring MVC + AngularJs

Original author: Jhades
  • Transfer
Hello, Habr.

The following is a translation for web application development. The stack described by the author demonstrates the interesting possibility of combining Java and JavaScript, and also allows you to take a fresh look at the creation of single-page web applications.

At the same time, we’ll ask if you want to see the translation of the following books on Spring and AngularJS on the shelf . Spring MVC and AngularJs technologies 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 fully functional, secure web application written using Spring MVC / AngularJs is located in this repository on GitHub.

We will consider the following issues:

  • Spring MVC + Angular Single Page Web Architecture
  • How to structure your web UI with Angular
  • What JavaScript / CSS libraries work well with Angular
  • How to build a native REST API using Spring MVC
  • Protecting REST APIs with Spring Security
  • Comparison of this approach with others, where the whole project is implemented in Java?


Spring MVC + Angular Single-Page Web Application Architecture

Enterprise environment applications that require intensive work with forms are best done as single-page web applications. The main idea that sets them apart from more traditional server architectures is to create a server in the form of a set of reusable REST services that do not preserve state. 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 template and contains all the presentation logic shared by the presentation level, controller level, and client services level. When the application is launched, the client and server will exchange only 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, the need for a separate stack of SOAP web services disappears.

From the point of view of DDD, the domain model remains at the machine interface, in the same place as the service level and persistence level. Only DTOs are transmitted over the network , but not a domain model.

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

The client part should line up around the model related to the presentation (and not to the subject area). Here, only presentation logic should be handled, but not business logic. The client side has three levels:

The presentation

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

Log In
New user?


The control level (controller)

The control level consists of Angular controllers that glue together data that is extracted, 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 should be repeated on the machine interface (at the service level) for security reasons, since client validation is easy to bypass.

Client Service Layer

Angular controllers can implement 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 get the client part running.

What JavaScript / CSS libraries should complement Angular?

Angular already provides a significant portion of the functionality needed to create the client side of your application. Here are some interesting add-ons for Angular:

  • Yahoo PureCSS Library . It is written in pure CSS, provides convenient design with the help of themes available in it and weighs only 4k. Its Skin Builder component makes it easy to generate a theme based on the primary color. This is a BYOJ (Bring Your Own Javascript) solution for writing Angular-style code.
  • Library for data operations in the style of functional programming. This library seems to boast excellent support and documentation unrivaled since lodash .


Armed with these two libraries and Angular, you can build almost any application with forms, almost nothing more is required. Depending on the specifics of your project, some other libraries may come in handy:

  • It’s convenient to have a module system like requirejs , but since the Angular module system does not handle file extraction, there is some overlap when declaring dependencies of requirejs and angular modules.
  • CSRF Angular module that prevents cross-site request forgery attacks.
  • Internationalization module


How to build a REST API machine interface using Spring MVC

This machine interface contains the usual layers:

  • Routing level: determines which service entry points correspond to specific HTTP URLs and how parameters will be read from the HTTP request
  • Service level: contains only business logic (for example, provides validation), defines the scope of business transactions
  • Retention level: Displays the database on domain objects stored in memory and vice versa


Currently, the best Spring MVC configuration involves only using a Java configuration. Even already, in general, is not required. See here for an example of a fully configured application that uses only Java. Service and persistence levels are created using the regular DDD model, so let's pay attention to the routing level. Routing Level The same Spring MVC annotations that are used to create the JSP / Thymeleaf application can also be used in developing the REST API.web.xml








The big difference is that the controller methods do not return a String object that determines which presentation template to display. Instead, the @ ResponseBody annotation is used, 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 must be annotated , it is best to annotate the entire class . 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 header specified by the client. Shown here is a pair of controllers that have error handling configured. How to secure a REST API with Spring Security@ResponseBody
@RestController


Accept






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 fallback option, as well as enable protection against CSRF and the ability to rigidly set 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 a successful login, it will assign a session cookie to browser clients, but it will also work with other clients, supporting rollback to normal HTTP in cases where credentials will be transmitted using the Authorization HTTP header.

According to OWASP recommendationsREST services can be programmed with minimal state preservation (all server status information is limited to the session cookie used for authentication). This is to prevent credentials from being sent over the network with every request.

Here is an example REST API security configuration:

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 authentication into account only in the security context, and the authorization strategy is selected depending on the security requirements of the API. If you need subtle control over authorization, check out Spring Security ACLs access control lists and see if they are suitable for the task you are facing.

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

Comparing the Spring MVC / Angular Stack with Other Common Variants

This way of using JavaScript in the client part and Java in working with the database simplifies the workflow and improves its productivity.

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

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

Increase productivity or full-stack development?

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

The greatest gain in productivity is achieved when the same developers write both the client side in JavaScript and the machine interface in Java, since most features usually require simultaneous changes both there and there.

The potential drawback of this approach is this: these developers should know HTML, CSS, and JavaScript, but in recent years this competence has been found more and more.

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

Conclusions

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 improves 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 cookies) by definition saves us from a whole class of bugs.

Additionally, I propose to read on github with this app.

Only registered users can participate in the survey. Please come in.

AngularJS + Spring MVC

  • 25.7% Interested in translating a book on AngularJS 107
  • 15.6% Interested in translating a book on Spring 65
  • 50.7% Interested in translating both books 211
  • 7.9% Thank you not 33

Also popular now: