Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Remote Transactional Views for Cloud APIs

DZone's Guide to

Remote Transactional Views for Cloud APIs

This sample web app puts remote transactional views on display so you can get a good look at how web and cloud architecture can work together.

· Cloud Zone
Free Resource

Are you joining the containers revolution? Start leveraging container management using Platform9's ultimate guide to Kubernetes deployment.

In a previous DZone link, I described an approach where the services identified have the potential to be developed as clean code. Interestingly, the analysis presented lends itself to a clean web architecture with lightweight services (APIs) on the cloud and views on a remote server. In this article, I present a starter implementation of some of the services using Spring Boot (deploy to Cloud Foundry) and then present transactional views using Angular UI (deploy to a remote server) for the services.

The sample web application I considered in the previous link was: Order pizza for Home Delivery.

For this starter demo, I have selected two services identified in the above application:

AddPizzaItemToUserCart:

package orderpizza.pizza;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;

import orderpizza.pizza.PizzaEntity;
import orderpizza.pizza.PizzaRepository;



/**
 * Spring Boot REST controller for the services
 * addPizzzaItemToUserCart and getUserCartPizzaItemsCount
 * Transactions are persisted using Hibernate JPA
 *
 * @author Nalla Senthilnathan (https://github.com/mapteb/approach2cleancode)
 *
 */

@RestController
public class PizzaController {

@Autowired
private PizzaRepository pizzaRepository;


@CrossOrigin
@RequestMapping(value="/pizza", method = RequestMethod.POST)
ResponseEntity<String> addPizzzaItemToUserCart(@RequestBody PizzaEntity input) {

        //TODO: identify the user and add pizza to user's cart using PizzaRepository
        //For this demo use customerid=1
        //if(pizzaRepository.findByCustomerid(1)!=null && 
//pizzaRepository.findByCustomerid(1).size()>10)pizzaRepository.deleteAll();

input.setCustomerid(1);
        PizzaEntity pe =pizzaRepository.save(input);

        return ResponseEntity.noContent().build();
    }

@CrossOrigin
@RequestMapping(value="/cart/items/count", method = RequestMethod.GET)
ResponseEntity<String> getUserCartPizzaItemsCount() {

        //TODO: identify the user and get pizza items count in 
//the user's cart using PizzaRepository
        //For this demo use customerid=1

        int count = pizzaRepository.findByCustomerid(1).size();

        String res = "{\"count\":"+count+"}";

        return new ResponseEntity<String>(res, HttpStatus.OK);
    }

@ExceptionHandler({ Exception.class })
public ResponseEntity<String> handleAll(Exception ex, WebRequest request) {
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}

}


And getPizzaItemsCountFromUserCart:

package orderpizza.pizza;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.WebRequest;

import orderpizza.pizza.PizzaEntity;
import orderpizza.pizza.PizzaRepository;



/**
 * Spring Boot REST controller for the services
 * addPizzzaItemToUserCart and getUserCartPizzaItemsCount
 * Transactions are persisted using Hibernate JPA
 *
 * @author Nalla Senthilnathan (https://github.com/mapteb/approach2cleancode)
 *
 */

@RestController
public class PizzaController {

@Autowired
private PizzaRepository pizzaRepository;


@CrossOrigin
@RequestMapping(value="/pizza", method = RequestMethod.POST)
ResponseEntity<String> addPizzzaItemToUserCart(@RequestBody PizzaEntity input) {

        //TODO: identify the user and add pizza to user's cart using PizzaRepository
        //For this demo use customerid=1
        //if(pizzaRepository.findByCustomerid(1)!=null && 
//pizzaRepository.findByCustomerid(1).size()>10)pizzaRepository.deleteAll();

input.setCustomerid(1);
        PizzaEntity pe =pizzaRepository.save(input);

        return ResponseEntity.noContent().build();
    }

@CrossOrigin
@RequestMapping(value="/cart/items/count", method = RequestMethod.GET)
ResponseEntity<String> getUserCartPizzaItemsCount() {

        //TODO: identify the user and get pizza items count in 
//the user's cart using PizzaRepository
        //For this demo use customerid=1

        int count = pizzaRepository.findByCustomerid(1).size();

        String res = "{\"count\":"+count+"}";

        return new ResponseEntity<String>(res, HttpStatus.OK);
    }

@ExceptionHandler({ Exception.class })
public ResponseEntity<String> handleAll(Exception ex, WebRequest request) {
return new ResponseEntity<String>(HttpStatus.INTERNAL_SERVER_ERROR);
}

}


I have implemented these as Spring Boot REST services and deployed to Cloud Foundry:

  • http://springboot-rest-jpa-nsorg.cfapps.io/pizza (POST)

  • http://springboot-rest-jpa-nsorg.cfapps.io/cart/items/count (GET)

I have implemented the views customizePizza.html ...

<!doctype html>
<html>
<head>
<title>Order Pizza / Customize Pizza</title>
<link href="//springboot-rest-jpa-nsorg.cfapps.io/css/angular-bootstrap.css" rel="stylesheet">
<script src="//springboot-rest-jpa-nsorg.cfapps.io/js/angular.min.js"></script>

<script src="//springboot-rest-jpa-nsorg.cfapps.io/js/customizePizza.js"></script>


</head>

<body ng-app="pizzaApp"  >
<div class="container">
<h3>Order Pizza</h3>
<h4> / Customize Pizza</h4>
<hr/>
<div class="small text-primary">Demo of static files (HTML5 + Angular UI) in GitHub<br/>
calling the following REST services deployed in Cloud Foundry.<br/>
http://springboot-rest-jpa-nsorg.cfapps.io/pizza (POST)<br/>
http://springboot-rest-jpa-nsorg.cfapps.io/cart/items/count (GET)</div><br/>
<div id="cntrl2" ng-controller="pizza as pizza">
<form name="customizePizzaForm" bakaction="reviewCart.html" ng-submit="submitForm()">
<table>
<tr><td>&nbsp;</td><td>&nbsp;</td></tr>
<tr><td>Select Pizza Pie Type:</td><td></td></tr>
<tr><td><input type="radio" name="pizzaType" ng-model="pizza.type" value="ThinCrust">Thin Crust&nbsp;<input type="radio" name="pizzaType" ng-model="pizza.type" value="DeepDish">Deep Dish</td><td></td></tr>
<tr><td>Select Pizza Size:</td><td></td></tr>
<tr><td><input type="radio" name="pizzaSize" ng-model="pizza.size" value="Personal">Personal&nbsp;<input type="radio" name="pizzaSize" ng-model="pizza.size" value="Medium">Medium&nbsp;<input type="radio" name="pizzaSize" ng-model="pizza.size" value="Large">Large</td><td></td></tr>
<tr><td>Select Additional Pizza Toppings:</td><td></td></tr>
<tr><td><input type="checkbox" name="pizzaTopping" ng-model="pizza.toppingMushroom" value="Mushroom">Mushroom&nbsp;<input type="checkbox" name="pizzaTopping" ng-model="pizza.toppingPepperoni" value="Pepperoni">Pepperoni&nbsp;<input type="checkbox" name="pizzaTopping" ng-model="pizza.toppingSpinach" value="Spinach">Spinach</td><td></td></tr>
<tr><td>&nbsp;</td><td>&nbsp;</td></tr>
<tr><td><input type="submit" name="SubmitButton" value="Submit"/></td><td class="{{pizza.status_color}} text-left">{{pizza.status}}</td></tr>
</table>
</form>
</div>


</div>

</body>
</html>


... and reviewCart.html ...

<!doctype html>
<html>
<head>
<title>Order Pizza / Customize Pizza / Review Cart</title>


<!-- meta http-equiv="cache-control" content="max-age=0" />
<meta http-equiv="cache-control" content="no-cache" />
<meta http-equiv="expires" content="0" />
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
<meta http-equiv="pragma" content="no-cache" / -->


<link href="//springboot-rest-jpa-nsorg.cfapps.io/css/angular-bootstrap.css" rel="stylesheet">
<script src="//springboot-rest-jpa-nsorg.cfapps.io/js/angular.min.js"></script>

<script src="//springboot-rest-jpa-nsorg.cfapps.io/js/cart.js"></script>

</head>

<body ng-app="cartApp"  >
<div class="container">
<h3>Order Pizza</h3>
<h4> / Customize Pizza / Review Cart</h4>
<hr/>
<div id="cntrl1" ng-controller="cart as cart">
<table>
<tr><td>Cart Items Count: {{cart.items.count}}</td><td></td></tr>
</table>


</div>


</div>
<!--  script src="/js/ui-bootstrap-tpls-2.3.2.min.js" type="text/javascript"></script -->




</body>
</html>


... using Angular UI and deployed to GitHub. (The source for the services and the views are in GitHub).

Here is a demo of the remote views calling the services.

Some key features to note here are:

  • The service (API) code has no view related concerns.

  • Each service is lightweight implementing just one feature of the requirement.

  • The views are equally lightweight with no business logic.

  • Angular and Spring frameworks have simplified the CORS and the JSON issues.

Also see: AngularJS + Cloud Endpoints -- A Recipe for Building Modern Web Applications.

Using Containers? Read our Kubernetes Comparison eBook to learn the positives and negatives of Kubernetes, Mesos, Docker Swarm and EC2 Container Services.

Topics:
clean code ,web app architecture ,cloud ,tutorial ,cloud api

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}