Second Microservice¶
Introduction¶
Please review my first Microservice page before going into this one, as it contains a lot of contextual information about the use cases of Microservices and the technicalities that are implied.
New problem¶
In order to set up the second microservice, I noticed right away that I was facing an interesting challenge. I found out that my implementation of the first microservice (while it did run successfully) was not the best. The reason for this is I want to use a single repository for my code base. This was not possible in the way I set up my first microservice, thus a lot of refactoring had to be done.
Refactorama¶
The refactoring that had to be done was quite convoluted; I initially thought I could simply drag the folders around and hope IntelliJ was smart enough to figure it out on its own, sadly, this was not the case.
To ready my project for multi-module use, I had to create folders at the root level in which the services could be placed in.
I then had to add sources on my own, redefine what some of the folders where meant for. With some manual naming refactoring & re-running my maven file and creating new ones seemed to have resolved these issues after a pain staking time. Only at a face value though, because I knew my Ci/CD was not ready for this change, which I ultimately also had to reconfigure parts of to make it work.
Execution¶
The creation of the second Microservice, after all the refactoring, was much more straight forward than the first. I created my logic layer, repository & rest api. I then later implemented rabbitMQ. The gameservice, currently, is fairly simple–it contains a challenge and a title.
The SpringServer class serves the REST API. In my resources I have a data.sql file which fills the database up with a few example records I can use to test simply test with.
The API returns a RESTful HateOAS Level 3 Maturity call. What makes it level 3? From my understanding, it is mostly about sending links back which are supported operations. I created an addition to have pwreset as an option, to demonstrate that the API is mature.
The RabbitMQ portions are still in construction and will be covered with another learning goal page.
I used a Dockerfile for the implementation of this microservice, the dockerfile serves maven and the jar file.
In order to use mySQL in a Dockerstack, I added a docker compose file–which ultimately will be rewritten to a Kubectl configuration. Here is the compose file:
version: "3.8"
services:
mysqldb:
image: mysql:5.7
restart: unless-stopped
env_file: .env
environment:
- MYSQL_ROOT_PASSWORD=$MYSQLDB_ROOT_PASSWORD
- MYSQL_DATABASE=$MYSQLDB_DATABASE
ports:
- 3306:3306
volumes:
- db:/var/lib/mysql
gameService:
depends_on:
- mysqldb
build: ./gameService/
image: ramses-gameservice:latest
container_name: ram
restart: on-failure
env_file: .env
ports:
- 9521:9521
environment:
SPRING_APPLICATION_JSON: '{
"spring.datasource.url" : "jdbc:mysql://mysqldb:$MYSQLDB_DOCKER_PORT/$MYSQLDB_DATABASE?useSSL=false",
"spring.datasource.username" : "$MYSQLDB_USER",
"spring.datasource.password" : "$MYSQLDB_ROOT_PASSWORD",
"spring.jpa.properties.hibernate.dialect" : "org.hibernate.dialect.MySQL5InnoDBDialect",
"spring.jpa.hibernate.ddl-auto" : "update"
}'
volumes:
- .m2:/root/.m2
stdin_open: true
tty: true
volumes:
db:
And here is an overview with all of the supported API calls sent to the GameController:
@GetMapping
public ResponseEntity<Resources<GameResource>> all() {
final List<GameResource> collection = _GameCollection.getAllGames().stream().map(GameResource::new).collect(Collectors.toList());
final Resources <GameResource> resources = new Resources < > (collection);
final String uriString = ServletUriComponentsBuilder.fromCurrentRequest().build().toUriString();
resources.add(new Link(uriString, "self"));
return ResponseEntity.ok(resources);
}
@GetMapping("/{id}")
public ResponseEntity<GameResource> get(@PathVariable final int id) {
return _GameCollection.getGame(id).map(a-> ResponseEntity.ok(new GameResource(a))).orElseThrow(()-> new NoResultException());
}
@PostMapping
public ResponseEntity<GameResource> post(@RequestBody final Game GameFromRequest) {
final Game Game = new Game(GameFromRequest);
_GameCollection.createGame(Game);
final URI uri = MvcUriComponentsBuilder.fromController(getClass()).path("/{id}").buildAndExpand(Game.getId()).toUri();
return ResponseEntity.created(uri).body(new GameResource(Game));
}
@PutMapping("/{id}")
public ResponseEntity<GameResource> put(@PathVariable("id") final int id, @RequestBody Game GameFromRequest) {
final Game Game = new Game(GameFromRequest, id);
Game.update();
final GameResource resource = new GameResource(Game);
final URI uri = ServletUriComponentsBuilder.fromCurrentRequest().build().toUri();
return ResponseEntity.created(uri).body(resource);
}
@DeleteMapping("/{id}")
public ResponseEntity<?> delete(@PathVariable("id") final int id) {
return _GameCollection.getGame(id).map(p-> {
_GameCollection.deleteGame(id);
return ResponseEntity.noContent().build();
}).orElseThrow(()-> new NoResultException());
}
Validation¶
In this screenshot I’m using Postman to test if the API call works in my local Docker setup: