Skip to content

Demo project for a µservice consuming a 3rd party service and exposing combined information in a new REST endpoint. Java EE (WildFly) and Vert.x versions.

License

Notifications You must be signed in to change notification settings

MiguelGL/popular-purchases-demo

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A Demo "Recent Purchases" Microservice

Introduction

The idea is to develop a backend-only microservice aggregating data fetched from an already available, third-party API providing information on "users", "products" and "purchases". A single endpoint is exposed combining information so that a list of "recent purchases" a user made is returned including information on the products the user purchased and who else did purchase them, sorted by the number of product purchases.

Information is cached so that further calls take the shortest time and also requirements on not found users are implemented with a custom HTTP response (status code and error message).

Requirements

  • Operating System agnostic.
  • JDK 8.
  • Maven 3.3.9.
  • A decent terminal application and the tools above available in the user PATH.

The requirements above can also be satisfied (vs. plain download + install) using any package management tool for the OS running the demo. Development was made on a Mac and maven was installed via HomeBrew.

I have used the NetBeans IDE for development which ships great Maven integration. But any tool can be used to navigate and study the source code since it is a Maven standard (multi-project) build.

Same Functionality, Two Different Implementations

I initially wrote a Java EE based solution including a separate client for the existing API. However I ended up feeling the solution was maybe a bit over engineered for the sole purpose of a single API endpoint. I would take an approach like this one for larger projects but in case it would be considered a "one hammer for all problems", over-engineered solution I wrote a separate implementation on a more straightforward approach based on Vert.x.

The source code includes -and this document explains- both solutions.

A Note on Caching

Both solutions cache succesful request responses to the consumed API. However these are cached forever because there is no way the consumed service notifies relevant changes or accesses the used cache so that invalidations can take place.

A real-world production solution should take this into account and include some means to allow for invalidating cache entries. Neither of the solutions here have this functionality (though implementing it could be seamless).

Build

In order to build everything, and provided Maven and the JDK tools are in the users path:

  • cd to the toplevel directory.
  • From there, call $ mvn clean install -DskipTests=true
  • Wait until Maven downloads the whole Internet for the first time (subsequent builds won't take this long).

Appoach #1: Java EE Based Solution

In order not to require a pre-existing deployment of a Java EE (spec. version 7) application server, I have used the recent WildFly-Swarm toolset. The idea is to build an standalone bundle that when run via $ java -jar ... starts the required application server components together with the developed solution.

In order to run it:

$ cd <project-root-level>
$ mvn clean install -DskipTests=true # If not built already
$ cd javaee/server
$ mvn wildfly-swarm:run # Please see (*)

This will run the solution, listening on all interfaces (0.0.0.0) and on port 8081. You can now call the endpoint, for example:

$ curl http://localhost:8081/api/recent_purchases/Kade6

(*) As explained above the solution should also be runnable via a command like:

$ java -jar target/popular-purchases-server-1.0.0.Final-swarm.jar

But this fails due to an ambiguous resolution for injected CDI beans problem. This is most likely a problem with the WildFly Swarm toolset used, which is at the moment a candidate release. I however settled to use it for this demo because running via the Maven plugin works.

I make a rather extensive use of Project Lombok so that lots of the required boilerplate code (constructors, getters and setters mostly) in Java classes is autogenerated at build time. I am a huge fan of this tool and have been using it in production projects for years.

A Dedicated, Separate Downstream API Client

I decided to model the data types and HTTP client for the consumed API into a separate project within the javaee/daw-purchases-client directory. The intention is to abstract this API via a dependency the server project includes, separating concerns.

This client uses Jackson annotated model clases (package com.mgl.demo.popularpurchases.dawps.client.model) and uses the RestEasy Proxy Client feature to build a REST client for the DAW API from the JAX-RS spec. in com.mgl.demo.popularpurchases.dawps.client.ApiSpec.

The core class in this project is com.mgl.demo.popularpurchases.dawps.client.DawPurchasesClient. This also configures the underlying HTTP client with sensible defaults that could be however made configurable as desired.

The project includes some basic Integration Tests:

$ cd <project-root-level>
$ cd javaee/daw-purchases-client
$ mvn clean install -DskipTests=true # If not built already
$ mvn test # Requires a network connection and the DAW API reachable

Model classes have all final attributes and matching constructors because after all these are just designed to be DTOs and can perfectly be inmutable for our purposes.

Java EE Server Project

This is the main project implementing our endpoint. It depends on the client just explained.

The most relevant classes are:

  • com.mgl.demo.popularpurchases.server.rest.PopularPurchasesResource: This is the endpoint JAX-RS implementation. It is basically designed to deal with the REST exposure and calls the service below for the business logic. It also deals with HTTP cache including an ETag response header whose value can be used for further client calls via a If-None-Match request header.

  • com.mgl.demo.popularpurchases.server.service.PopularPurchasesService: This class implements the core functionality that ends up being exposed in the REST resource above. I am making an extense use of Java 8 features such as lambdas and streams. Also, in order to perform paralell operations, I am using Java 8's CompletableFuture (a promises- like API) together with Java EE 7's ManagedExecutorService.

  • com.mgl.demo.popularpurchases.server.service.CacheAwareDawPurchasesClient: This is a CDI bean that decorates the API client explained before so that it has caching features. Calls to the delegated client are intercepted using JCache API features (@CacheResult and @Interceptors({CacheResultInterceptor.class}) combined). The underlying cache implementation is Infinispan, for which WildFly Swarm ships support out-of-the-box.

  • Selectively mapping method call exceptions into HTTP response codes is made via the com.mgl.demo.popularpurchases.server.rest.*ExceptionMapper classes again using JAX-RS features.

Appoach #2: Vert.x Based Solution

Just in case approach #1 seemed a bit too much for a single endpoint (I would however go that route for larger projects) I decided to write a much more straightforward solution based on Vert.x. I have used this project for several production highly-networked systems with fun and success.

A self-runnable jarfile is built:

$ cd <project-root-level>
$ cd vertx
$ mvn clean install -DskipTests=true # If not build already
$ java -jar target/vertx-popular-purchases-server-1.0.0.Final-fat.jar

This will run the solution, listening on all interfaces (0.0.0.0) and on port 8002. You can now call the endpoint, for example:

$ curl http://localhost:8002/api/recent_purchases/Kade6

This is a much more direct solution for which I even did not write any model POJOs, but settled to use Jackson's (shipped with Vert.x for JSON support) classes directly. For a larger solution or handling a richer data model I would certainly use dedicated model classes just like for the Java EE solution.

The most relevant (and almost single) piece of code here is com.mgl.demo.popularpurchases.vertx.RecentPurchasesHandler. It all starts at its only public method void handle(RoutingContext routingContext). The code leverages Vert.x's async nature to accomplish paralell requests. Also, I decided to use the alternative RxJava oriented platform API instead of the classical callback. So every async operation (API calls) is built on top of Observables and related operations and classes.

For caching support I settled for Vert.x's LocalMap shared data feature which is capable of persisting immutable values visible only to the JVM the instance is running at. If a distributed cache was wanted the cluster-enabled shared data structures could be used.

Final

There are other implementation details I have not got into in this document. I would suggest navigating the source code (a stock NetBeans 8.1 installation would serve perfectly as it understands Maven projects natively). The "most relevant classes" explained in this document can be a good starting point for examination.

There are not many code comments around but for the most important operations implementing the core functionality. I expect the code to turn out communicative enough.

Also looking into the several pom.xml files can provide more insights on dependencies and the build structure in general. I tried to write them with as much care as the code itself as I believe build processes and automation are of the same importance and deserve the same attention and maintainability.

About

Demo project for a µservice consuming a 3rd party service and exposing combined information in a new REST endpoint. Java EE (WildFly) and Vert.x versions.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages