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

Create Production-Grade Java Microservice Architecture With JHipster and Couchbase

DZone 's Guide to

Create Production-Grade Java Microservice Architecture With JHipster and Couchbase

Learn about the architecture of microservices and NoSQL with Couchbase to build Java microservices with JHipster.

· Microservices Zone ·
Free Resource

Every successful developer needs to keep up to date with cutting-edge new technologies and last emerging trends, trying them, playing with them, seeing if they can fit in your project, and how you can improve it.

But how can one achieve that in a fast-changing world where new technologies and frameworks are born daily? If you are a Java full-stack developer, you are very lucky: JHipster is the solution to quickly learn new technologies. It is a Yeoman generator, a scaffolding system, that helps you generate complete applications combining one of the best frameworks in web development: Spring Boot backend and Angular or React frontend.

Of course, you can use Spring Initialzr, or implement everything by yourself, as I'm always told. But to successfully implement those combinations is not trivial at all. You need to spend a lot of time studying each concept then tries to glue everything up, without forgetting tests, of course, which can be very expensive and error-prone. Most of all, it can discourage you or at best oblige you to focus on one of the frameworks.

JHipster builds fully tested applications quickly and easily, along with best practices, methodologies, and strategies. It proposes a lot of options (monolith or microservices, SQL or NoSQL, Session, JWT, or OAuth2 authentication, websockets, caches, Docker, Kubernetes...). This helps you try the best technologies out there and choose the most suitable combinations in your use case.

Today we are going to focus on some of the most important new architectural concepts: microservices and NoSQL with Couchbase.

Why Couchbase?

Couchbase is a NoSQL document-oriented database. With document databases, you have a schemaless design which allows you to change your data freely and easily. You can also store the whole structure in a single document, avoiding lots of unnecessary joins, which means naturally faster read and write operations.

Couchbase exposes a fast key-value store and a powerful query engine along with built-in indexers to query data with N1QL, an SQL like language for JSON documents. With his masterless distributed architecture, it is very easy to scale. It can deliver over millions of operations per second without the need of third-party cache to perform. Couchbase comes also with built-in Full-text search, analytics engine, and a complete mobile solution.

Why Microservices?

Microservice architecture is a pattern of developing software systems that focus on building applications as a set of small, modular, and loosely coupled services. Enabling easier continuous delivery, better testability, scalability, and improved fault isolation. Each service can be written in different languages and may use different data storage techniques, which enables to organize development around multiple feature teams.

But microservices comes with some challenges: decomposing the application into services can be very complicated, and is very much an art. Developers also have to deal with the additional complexity of a distributed system.

Hopefully, JHipster handles most of the complexity of microservices: Service discovery and configuration with Consul or JHipster Registry (Netflix Eureka, Spring Cloud Config Server and monitoring dashboard), load balancing with Netflix Ribbon, fault tolerance with Netflix Hystrix, centralized logging and monitoring with jhipster-console (Elasticsearch, Logstash, and Kibana personalized stack), etc.

Creating a Brewery Microservice

Prerequisite

yarn global add generator-jhipster

Generate an API Gateway

In order to access our different services in a microservice architecture, we're going to need an API Gateway. In a terminal window:

mkdir couchbase-jhipster-microservices-example
cd couchbase-jhipster-microservices-example
mkdir gateway
cd gateway
jhipster

JHipster asks about the type of application you want to create and what features you want to include. Use the following answers in order to generate a gateway with Couchbase support.

generate gateway

Generate a Brewery Microservice Application

In couchbase-jhipster-microservices-example, create a brewery directory, then run JHipster to generate a microservice with Couchbase database with the following answers:

generate brewery

Generate Brewery Entities

Create a file brewery.jh in couchbase-jhipster-microservices-example directory with the following JDL (JHipster Domain Language):

entity Beer {
    name String required,
    category String required,
    description String,
    style String,
    brewery String,
    abv Float,
    ibu Integer,
    srm Integer,
    upc Integer,
    updated LocalDate
}

entity Brewery {
    name String required,
    description String,
    address String required maxlength(200),
    city String,
    code String,
    country String,
    phone String pattern(/[0-9- .]+/),
    state String,
    website String,
    updated LocalDate
}

paginate Beer with pager
paginate Brewery with infinite-scroll

In a terminal window, run the following commands:

cd brewery
jhipster import-jdl ../brewery.jh
cd ../gateway
jhipster entity brewery
jhipster entity beer

When asked to overwrite files, always answer a.

gateway entity


Run Your microservices Architecture

In order to run our architecture, we need to start the following:

  1. JHipster Registry
  2. Keycloack: an open source dentity and access management solution
  3. Couchbase server
  4. Brewery microservice
  5. Gateway

Fortunately, JHipster has a docker-compose sub-generator that will start all necessary services without headache. But first, we need to build our applications. In couchbase-jhipster-microservices-example:

cd gateway
./mvnw package -Pprod dockerfile:build -DskipTests
cd ../brewery
./mvnw package -Pprod dockerfile:build -DskipTests
cd ..mkdir docker-compose && cd docker-compose

Now, we can generate docker-compose.yml using the following command and answers:

jhipster docker-compose


docker-composeIn order to make our app work works with a local keycloak, we need to add to your hosts file (Windows: C:\Windows\System32\drivers\etc\hosts, Mac/Linux: /etc/hosts) the following line: 

127.0.0.1 keycloak

Before starting everything, make sure you have configured Docker with enough memory and CPUs, then run:

docker-compose up

Once everything finishes starting, open a browser to Gateway (http://localhost:8080/), click on account, then sign in.

gateway log off

You should be redirected to keycloak, log in using admin for both user and password.keycloack

You'll be back then to the gateway, where you can see administration interfaces, change language, view and edit your entities.gateway logged in

entities

JHipster-registry

Now open your browser to http://localhost:8761/. You'll be automatically signed in since you already done that in keycloak, which is the magic of oauth2 authentication.

jhipster registry

Apart from being the backbone of your microservice application, as it is a discovery server with Eureka that handles routing, load balancing and scalability, and a configuration server with Spring Cloud Config Server that provides runtime configuration of your applications, JHipster registry is also an administration server, where you can visualize your application instances, health, metrics, and logs...

How Does It Work?

First, you need to access to brewery-couchbase instance. In order to do so, update docker-compose/docker-compose.yml to publish its administration interface port with the following:

brewery-couchbase:
  build:
    context: ../brewery/src/main/docker
    dockerfile: couchbase/Couchbase.Dockerfile
  environment:
    - BUCKET=brewery   
  ports:
    - 8091:8091

Let's apply our changes, on docker-compose directory:

docker-compose up -d

When everything is up, open the Couchbase administration interface on http://localhost:8091/, then log in using Administrator as user and password as password. Open brewery bucket Documents.

couchbase documents

Couchmove

As you can see, your bucket is already populated with some documents: JHipster populate boostrap documents using Couchmove, a Java data migration library for Couchbase, widely inspired from Flyway, as it strongly favors simplicity and convention over configuration.

You can take a look on changelog files in brewery/src/main/resources/config/couchmove/changelog directory:

  • V0.1__initial_setup directory: contains user and authority documents, used by Spring Security to authenticate users.
  • V0__create_indexes.n1ql: creates needed indexes.

As Flyway or liquibase for SQL databases, Couchmove maintain changelog documents keeping tracks of executed changelogs.

Spring Boot and Spring Data Couchbase

JHipster generates a Spring Boot 2 application and uses Spring Data to access relational and non-relational databases. In our case, it is using Spring Data Couchbase to provides integration with the Couchbase Server database. But it goes much far by personalizing how it works:

By default, Spring Data Couchbase uses N1qlCouchbaseRepository.java which leverages N1QL only for paging or sorting findAll operations. All other operations are using views as you can see in the implementation of SimpleCouchbaseRepository.java. Since view indexes are always accessed from disk, they are not quite performant.

Let's see how JHipster improves this behavior:

@Configuration
@Profile("!" + JHipsterConstants.SPRING_PROFILE_CLOUD)
@EnableCouchbaseRepositories(repositoryBaseClass = CustomN1qlCouchbaseRepository.class, basePackages = "com.couchbase.example.brewery.repository")
@Import(value = CouchbaseAutoConfiguration.class)
@EnableCouchbaseAuditing(auditorAwareRef = "springSecurityAuditorAware")
public class DatabaseConfiguration {
    ...
    @Bean
    public Couchmove couchmove(Bucket couchbaseBucket) {
        log.debug("Configuring Couchmove");
        Couchmove couchMove = new Couchmove(couchbaseBucket, "config/couchmove/changelog");
        couchMove.migrate();
        return couchMove;
    }
    ...
}

DatabaseConfiguration class configures and start Couchmove migrations, and also enables Couchbase Repositories but with a custom base class: CustomN1qlCouchbaseRepository

public class CustomN1qlCouchbaseRepository<T, ID extends Serializable> extends N1qlCouchbaseRepository<T, ID> {
    ...
    @Override
    public <S extends T> S save(S entity) {
        return super.save(populateIdIfNecessary(entity));
    }
    ...
}

This class extends default repository to auto-generate document ID before saving, and enables usage of N1QL for all operations by implementing CustomN1qlRespository interface:

@NoRepositoryBean
public interface N1qlCouchbaseRepository<T, ID extends Serializable> extends CouchbasePagingAndSortingRepository<T, ID> {

    @Query("#{#n1ql.selectEntity} WHERE #{#n1ql.filter}")
    List<T> findAll();

    @Query("SELECT count(*) FROM #{#n1ql.bucket} WHERE #{#n1ql.filter}")
    long count();

    @Query("DELETE FROM #{#n1ql.bucket} WHERE #{#n1ql.filter} returning #{#n1ql.fields}")
    T removeAll();

    default void deleteAll() {
        removeAll();
    }
}

Ids are autogenerated using GeneratedValue annotation with a prefix using IdPrefix annotation, which by default the name of the class, separated by __ delimiter.

@Document
public class Brewery implements Serializable {

    private static final long serialVersionUID = 1L;

    public static final String PREFIX = "brewery";

    @SuppressWarnings("unused")
    @IdPrefix
    private String prefix = PREFIX;

    @Id
    @GeneratedValue(strategy = UNIQUE, delimiter = ID_DELIMITER)
    private String id;
    ...
}

Tests

We said earlier that JHipster generated applications are fully tested with unit and integration tests, but how does it work for integration ones?

For Couchbase, it is using Couchbase testContainers, which is a module extending testcontainers, a Java library that makes it easy to launch any Docker containers for the duration of JUnit tests, to automatically start an instance of Couchbase server, and configures it with necessary services, users, buckets and indexes.

Let's Populate Some Data

In order to populate some data, we will take advantage of Couchbase Sample buckets. Open Settings tab, Sample Buckets, then select beer-sample, and click on Load Sample Data. couchbase beer sample

You need to shape data in Spring Data Couchbase serialization format. To do so, open Query tab, and execute the following queries:

INSERT INTO brewery (KEY k, VALUE v)
   SELECT type || "__" || meta().id as k,
   {
      "name": name,
      "category": category,
      "description": description,
      "style": style,
      "brewery": brewery_id,
      "abv": abv,
      "ibu": ibu,
      "srm": srm,
      "upc": upc,
      "updated": STR_TO_MILLIS(updated),
      "_class" : "com.couchbase.example.brewery.domain.Beer"
   } as v
   FROM `beer-sample`
   WHERE type = 'beer';

INSERT INTO brewery (KEY k, VALUE v)
   SELECT type || "__" || meta().id as k,
   {
      "name": name,
      "description": description,
      "address": address[0],
      "country": country,
      "website": website,
      "code": code,
      "city": city,
      "phone": phone,
      "state": state,
      "updated": STR_TO_MILLIS(updated),
      "_class" : "com.couchbase.example.brewery.domain.Brewery"
   } as v
   FROM `beer-sample`
   WHERE type = 'brewery';

couchbase query

Here an example of an inserted beer document:

couchbase beer document

Now let's see how JHipster handles newly inserted documents. Navigates to Gateway, Entities, then open Brewery. JHipster loads 20 first breweries, and if you scroll down, it loads more while it proposes pages navigation for Beer entity. This is because we choose this behavior when generating entities with brewery.jh JDL.

paginate Beer with pager
paginate Brewery with infinite-scroll

Source Code

The source code for generated applications is available at tchlyah/couchbase-jhipster-microservices-example.

What's Next?

JHipster proposes an option that adds search capabilities on top of the database, with the help of Elasticsearch. But Couchbase has an out-of-the-box Full-Text Search feature that provides extensive capabilities for natural-language querying. Why not implement that support into JHipster? If you can help, don't hesitate to contribute!

If you have any questions, tweet me at @tchlyah or leave a comment below.

Topics:
couchbase ,jhipster ,tutorial ,microservices ,java ,open source

Opinions expressed by DZone contributors are their own.

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

{{ parent.tldr }}

{{ parent.urlSource.name }}