Skip to content

This repository implements an API, based on Kotlin and Spring Boot, that operates against JSON Placeholder.

License

Notifications You must be signed in to change notification settings

SaulEiros/album-explorer

Repository files navigation

Issues MIT License Live Demo LinkedIn

Table of Contents
  1. About The Project
  2. Project Implementation
  3. Usage
  4. Further Work
  5. Contribution

ABOUT THE PROJECT

This project is a technical demonstration of how to build a simple project using Kotlin and Spring Boot. This project also wants to reflect clean code and clean architecture techniques and best practices.

The goal of the app is to display all the albums hosted in JSON Placeholder API and the details of the photos in those albums.

(back to top)

BUILT WITH

The application was created using Spring Initializr based on Spring Boot 3.3.2 and java 21.

These are the tools with which the application has been built:

  • SpringBoot
  • Kotlin
  • Junit
  • Mockito
  • Swagger

(back to top)

How To Explore The Repository

The repository was tagged each time a significant change was made so that the entire development process could be explored.

These are the different tags available:

(back to top)

PROJECT IMPLEMENTATION

This section explores how different aspects of development were addressed for the reader's clarity.

(back to top)

GIT WORKFLOW

All the developments were done in a feature branch and merged to main once the branch scope was complete. Once the changes reach main, a new tag is created so that they can be easily located.

Also, Conventional Commits specification (see more) was followed.

Perhaps in a real scenario, merging the changes by squashing commits would have been appropriate, but in this case I have chosen to merge all commits so that the entire development process could be seen.

(back to top)

HEXAGONAL ARCHITECTURE

Hexagonal Architecture, also known as Ports and Adapters Architecture (read more), is an architectural pattern that aims to create applications with low coupling between its different components.

This allows its components to be easily replaced without compromising other parts of the software. In addition, it also facilitates the maintenance and extension of the code, as well as its testing.

It may seem that for such a simple application it is not necessary. But if, in the future, we would like to add more functionality, integrate with a database to create a real repository of images or add extra functionality, having developed the application following this pattern will make the process much easier.

The main code is separated into the following folder hierarchy:

main
├── AlbumExplorerApplication.kt
├── application
    ...
├── domain
    ...
└── infra
    ...
  • application: This layer contains the implementation of the application's business logic. It also defines interfaces of input adapters (services that implement the business logic) and output adapters (connections to external repositories and services).
  • domain: This layer implements the objects that define the core elements of the business.
  • infra: This layer implements the input and output adapters (The input and output connections with external services.)

(back to top)

TESTING

Unit tests for the application layer were developed using Junit 5 and Mockito. Also, TTD (see more) was followed, creating first the test suit and implementing the services later.

To track this during development, you can consult the commit history for the tags album-service-impl and photo-service-impl.

Additionally, to structure the tests, I have tried to follow the GIVEN/THEN/WHEN pattern (see more). Kotlin has an awesome language feature that allow us to use backticks to write freeform function names. This helps to declare test names that are descriptive of the purpose of the test.

Here is an example of such tests:

@Test
fun `GIVEN an albumId without photos WHEN photos by albumId are requested THEN an empty list is returned`() {
    // GIVEN
    Mockito.`when`(photoRepository.findByAlbumId(1L)).thenReturn(emptyList())

        // WHEN
        val result = photoService.find(1L)

        // THEN
        assertNotNull(result)
        assertTrue(result.isEmpty())
        Mockito.verify(photoRepository, Mockito.times(1)).findByAlbumId(1L)
}

(back to top)

PROJECT CONFIGURATION

There are a few properties configured for the smooth running of the project:

spring.application.name=album-explorer
springdoc.writer-with-order-by-keys=true
springdoc.swagger-ui.tags-sorter=alpha
json-placeholder.url=https://jsonplaceholder.typicode.com

The most relevant is json-placeholder.url, which is injected into the services that consume the JSON Placeholder API.

(back to top)

USAGE

PREREQUISITES

If you want to run the application, you must make sure that you have the following dependencies installed:

(back to top)

RUN THE APPLICATION

To run the application it is necessary to type the following commands:

gradle build
gradle bootRun

If you only want to run the test, run the following command:

gradle test

(back to top)

USE THE APPLICATION

To use the application you can access the Swagger UI. To do so, you can open a browser and go to this url:

http://localhost:8080/swagger-ui/index.html

You can also perform request to the API invoking directly the available endpoints:

Get a certain Photo.

http://localhost:8080/photos/{{id}}

{{id}} Should be a Long. It will return a 404 Error if the photo does not exist.

Get all Photos

http://localhost:8080/photos

You can use the query parameter albumId to retrieve all the pictures that belongs to that album.

http://localhost:8080/photos?albumId={{id}}

{{id}} Should be a Long. If the album does not exist, It will return an empty list.

Get a certain Album

http://localhost:8080/albums/{{id}}

{{id}} Should be a Long. It will return a 404 Error if the album does not exist.

Get all albums

http://localhost:8080/albums

(back to top)

DOCKER IMAGE

To facilitate the deployment of the application, if you have Docker on your system you can run the application with the following command:

docker-compose up -d

Once the container is up and running, you can access it as normal:

On the swagger front end:

http://localhost:8080/swagger-ui/index.html

By querying the api directly, as in this example:

http://localhost:8080/photos

Aditionaly, a Github Actions Workflow was created for build and publish the image every time a new change is pushed into main (see workflow).

You do not need to download the project for executing it in your local. In order to use the published image, just create a docker-compose.yml file like this:

services:
  backend:
    container_name: album-explorer-backend
    image: mreiros/album-explorer-backend:latest
    volumes:
      - ./src:/app/src
    ports:
      - 8080:8080

Once you have the file created, just run the following commands:

First You need to retrieve the image (also if You have it already and want to update to a new release):

docker pull mreiros/album-explorer-backend:latest

Then, You can start the application running the following command:

docker-compose up -d

(back to top)

LIVE DEMO

A live demo is also available without downloading the code. You can consult the swagger panel at the following link:

http://album-explorer.sauleiros.com/swagger-ui/index.html

Or make requests directly to the api as in this example:

http://album-explorer.sauleiros.com/photos

Note that HTTPS requests are not available. Enabling SSL connections in Swagger is a work in progress.

(back to top)

FURTHER WORK

Activate SSL on Swagger Connections:

There is currently a problem accessing the Swagger panel in the Live Demo. If accessed via https, it will not be possible to test operations.

Integrating a CI/CD flow in github:

It would be great to be able to integrate the changes automatically into the Demo server. For this, a CI/CD plan could be created using the pipelines offered by github.

Improving exception handling at the REST layer:

Currently, the exception handling done at the REST layer is limited and could be improved by providing for a larger volume of exceptions.

Contribution

The project has been developed for training purposes, so if you have any suggestions, you are more than welcome to leave a comment by opening an Issue in the repository or contacting me through my Linkedin.

(back to top)

About

This repository implements an API, based on Kotlin and Spring Boot, that operates against JSON Placeholder.

Resources

License

Stars

Watchers

Forks

Packages

No packages published