Semir Umut Kurt
Jan 3 · 11 min read


Hello folks. I want to take you a journey about Spring Boot, Hibernate, Integration Testing, Unit Testing, MVC, DTO, Mapstruct, Lombok, TDD, and CI. This journey will consist of 3 parts. Here is the outline of the first part:

  • UML Class Diagram
  • CircleCI configurations
  • Maven dependencies
  • Create models and short glances to Hibernate
  • Issue Tracking on GitHub and working with multiple branches
  • DTO Pattern
  • Create mappers with Maptstruct and short glances to Lombok
  • Spring Data JPA
  • Service Layer and short glances to Multi-Layered Architecture
  • Unit Testing, Integration Testing, MockMVC
  • Thymeleaf


Class diagrams clearly map out the structure of a particular system by modeling its classes, attributes, operations, and relationships between objects.

Classes, Attributes, and Relations


You can create your spring boot project either from here or your favorite IDE. I created the project using Intellij. Here is the 2 step I took to create the project:

Specify the project name, group, artifact and then select the dependencies

POM (Project Object Model) file of the project can be reached via


What is Version Control?
Version control is a program that allows you to manage changes over time. You can track revisions of your project’s assets.

During this project, I will use Git. There are other VCS tools if you want to learn about them then follow the link.

I recommend you to watch this video so that you will learn how to enable version control system, commit and contribute to the projects.

After creating the project, I shared it on GitHub and then did my initial commit. The default commit branch name is master.

write meaningful commit messages


What is an Issue Tracking System (ITS)?

Issue tracking systems, commonly referred to as ITS, are software applications that provide a ticketing system to record and follow the progress of every issue identified by a computer user until the issue is resolved.

issue/bug tracking on GitHub

There are ITS with the sole aim of issue/bug tracking. JIRA is the most popular one. In the upcoming tutorials, I am planning to use JIRA.

On GitHub, issues can be assigned to individuals and they can have a description.


Continuous integration (CI) is the practice of automating the integration of code changes from multiple contributors into a single software project. The CI process is comprised of automatic tools that assert the new code’s correctness before integration.

As a continuous integration tool, I used CircleCI. The alternatives are Jenkins, TravisCI, TeamCity.

A continuous integration tool pulls the commit in real-time, create an executable file and then run it. If there is an error in code, contributors will be notified by email.

A pipeline in a Software Engineering team is a set of automated processes that allow Developers and DevOps professionals to reliably and efficiently compile, build and deploy their code to their production compute platforms. The continuous integration process is a part of the pipeline


Go to CircleCI web page and then set up a project as shown plow

Then go to your project and create folder named .circleci Under that folder create a file named config.yml . Here is the content of config.yml

# Java Maven CircleCI 2.0 configuration file
# Check for more details
version: 2
# specify the version you desire here
- image: circleci/openjdk:8-jdk

# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at
# - image: circleci/postgres:9.4
working_directory: ~/repo
# Customize the JVM maximum heap limit
MAVEN_OPTS: -Xmx3200m

- checkout
# Download and cache dependencies
- restore_cache:
- v1-dependencies-{{ checksum "pom.xml" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run: mvn dependency:go-offline
- save_cache:
- ~/.m2
key: v1-dependencies-{{ checksum "pom.xml" }}

# run tests!
- run: mvn package

then commit your changes into the branch named circle-ci-config.

after pushing commit then merge the branch with master

git checkout master
git merge circle-ci-config
git push
merging branches and then committing changes

Why I am merging branches?
I personally open a new branch per issue so that I easily can see changes between different commits. I’m also merging the newly committed push with the master branch so that I can see the whole project in one branch.

Be careful with commit messages on Git. If you type Closes #N this will closed the N’th issue. (n is a positive integer)

Till now, what we have done is here


Sometimes called tiered architecture, or n-tier architecture, a multi-layered software architecture consists of various layers, each of which corresponds to a different service or integration. Because each layer is separate, making changes to each layer is easier than having to tackle the entire architecture.

What does a layered software architecture consist of?

  • Application layer
  • Data layer

For detailed information, please see

MVC Framework

MVC is short for Model, View, and Controller. MVC is a popular way of organizing your code. The big idea behind MVC is that each section of your code has a purpose, and those purposes are different. Some of your code holds the data of your app, some of your code makes your app look nice, and some of your code controls how your app functions.

Basically we can say that MVC is an implementation of Multi-Layered Architecture.

I followed the tiered architecture concept in this project. Let’s take a look into Models of MVC.


Models are basically correspondence of unique tables in the database. Not every table has a correspondent model but the reverse is true.

But wait a second… We have a different understanding of representing objects in a database. So how can we define the relations in OOP?

Good question… The answer is mappings.

Before moving on creating our models it would be good to talk about precious Hibernate and JPA.

Let’s take a look at the JPA specification first.

What is JPA?

JPA can be seen as a bridge between object-oriented domain models and relational database systems. Being a specification, JPA doesn’t perform any operation by itself. Thus, it requires implementation. So, ORM tools like Hibernate, TopLink, and iBatis implements JPA specifications for data persistence.

What is Hibernate?

Hibernate is an implementation of JPA. So, it follows the common standards provided by the JPA.

So what we should understand is that interaction with database and database operations are done using JPA and ORM framework.

Important Concepts Of Hibernate

  • There will be a session factory per database.
  • The SessionFacory is built once at start-up
  • It is a thread-safe class
  • SessionFactory will create a new Session object when requested


  • The Session object will get a physical connection to the database.
  • A session is the Java object used for any DB operations.
  • A session is not thread-safe. Hence do not share hibernate session between threads
  • A session represents a unit of work with database
  • A session should be closed once the task is completed


The models in this project are Recipe, Ingredient, Category, Note, and UnitOfMeasure.

public class Recipe extends BaseEntity {
private String description;
private Integer prepTime;
private Integer cookTime;
private Integer servings;
private String source;
private String url;
private String directions;
private Byte[] image;

@OneToOne(cascade = CascadeType.ALL)
private Note note;

@Enumerated(value = EnumType.STRING)
private Difficulty difficulty;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "recipe")
private List<Ingredient> ingredients = new ArrayList<>();

@JoinTable(name = "recipe_category", joinColumns = @JoinColumn(name = "recipe_id"), inverseJoinColumns = @JoinColumn(name = "category_id"))
private List<Category> categories = new ArrayList<>();

Above is the implementation of the Model object. Other model implementations can be seen here

In commit message don’t forget to close the issue. After committing the changes, merge the current working branch with master.


What is the point of using DTO?

We can hide secret fields or remove redundant fields when we are transferring the data remote interfaces (e.g., web services), where each call is an expensive operation.

public class RecipeDto {
private Long id;
private String description;
private Integer prepTime;
private Integer cookTime;
private Integer servings;
private String source;
private String url;
private String directions;
private Byte[] image;
private List<IngredientDto> ingredients = new ArrayList<>();
private Difficulty difficulty;
private NoteDto note;
private List<CategoryDto> categories = new ArrayList<>();

Above is the implementation of RecipeDto other DTO objects can be found

After implementing DTO please write meaningful commit messages, close the issue and merge the current branch to master.

10. MAPSTRUCT and Lombok

I guess you noticed that we should map the model object to DTO. Instead of writing converters manually we can use Maspstruct which reduces coding by hundreds of lines.

public interface RecipeMapper {
Recipe dtoToEntity(RecipeDto recipeDto);
RecipeDto entityToDto(Recipe recipe);

Here is all you should do to convert DTO to an entity or reverse conversion.

Other mappers can be found

On the other side, Lombok enables us to have cleaner POJO objects. It provides setter, getter, equal and hash code, all arg constructor, no-arg constructor, required arg constructor.


JpaRepository extends PagingAndSortingRepository which extends CrudRepository.

It provides us numerous database manipulation query methods and much more.

Architectural Overview of JpaRepository

data and application layer

Frequently used annotations:

  • The @Entity annotation will map this POJO into the database with all of its fields.
  • The @Id annotation marks the field as the primary key of the table.
  • The @GeneratedValue annotation practically sets the AUTO_INCREMENT option of the primary key to true. You can optionally add (strategy = GenerationType.AUTO) to achieve this.
  • The @Transactional annotation
  • The @Repository annotates classes at the persistence layer, which will act as a database repository. @Repository’s job is to catch persistence specific exceptions and rethrow them as one of Spring’s unified unchecked exceptions.
  • The @Sercvice annotates classes at the service layer. @Service indicates that it’s holding the business logic.
  • The @Controllerannotation can be applied to classes only. It’s used to mark a class as a web request handler. It’s mostly used with Spring MVC application.
public interface RecipeRepository extends JpaRepository<Recipe, Long> {}

Above shows RecipeRepository, other repositories are here


The service layer holds the business logic. I implemented Recipe service and tests here

Test-Driven Development

In test-driven development firstly you declare service/repository/controller method and return null. These methods like interface/abstract method the difference is that you implement them with one line by returning null as shown below:

method implementation in test-driven development

Then you go and write a test method that will fail in the beginning. After writing the test method return back to the method in the pic and implement it. After that re-run test method, it should run successfully!

TDD will make you think through your requirements or design before you write your functional code.

Unit Testing

class RecipeServiceImplTest {

RecipeServiceImpl recipeService;

RecipeRepository recipeRepository;

void setUp() {
recipeService = new RecipeServiceImpl(recipeRepository);

void getRecipes() {
Recipe recipe = new Recipe();
List<Recipe> recipes = new ArrayList<>();

assertEquals(recipes.size(), 1);

List<RecipeDto> returnedRecipes = recipeService.getRecipes();

assertEquals(returnedRecipes.size(), 1);
verify(recipeRepository, times(1)).findAll();
verify(recipeRepository, never()).findById(anyLong());


Integration Testing

class UnitOfMeasureRepositoryTestIT {

UnitOfMeasureRepository unitOfMeasureRepository;

void findByDescription() {
Optional<UnitOfMeasure> unitOfMeasureOptional = unitOfMeasureRepository.findByDescription("Teaspoon");
assertEquals("Teaspoon", unitOfMeasureOptional.get().getDescription());

SpringExtension introduced in Spring 5, is used to integrate Spring TestContext with JUnit 5 Jupiter Test. SpringExtension is used with JUnit 5 Jupiter @ExtendWith annotation as following.

Spring MockMVC to perform integration testing of Spring web MVC controllers. MockMVC class is part of the Spring MVC test framework which helps in testing the controllers explicitly starting a Servlet container.

MockMVC example

class IndexControllerTest {
RecipeService recipeService;

Model model;

MockMvc mockMvc;
IndexController controller;

void setUp() {
controller = new IndexController(recipeService);
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();

void getIndexPage() {
List<RecipeDto> recipes = new ArrayList<>();
recipes.add(new RecipeDto());

RecipeDto recipe = new RecipeDto();



ArgumentCaptor<List<RecipeDto>> argumentCaptor = ArgumentCaptor.forClass(List.class);

String viewName = controller.getIndexPage(model);

assertEquals("index", viewName);
verify(recipeService, times(1)).getRecipes();
verify(model, times(1)).addAttribute(eq("recipes"), argumentCaptor.capture());
List<RecipeDto> setInController = argumentCaptor.getValue();
assertEquals(2, setInController.size());

void mockMvc() throws Exception {

I think I mentioned all the important parts and since it has been a long tutorial so that I decided to cut it short. All other branches can be reached here

After doing all steps, when you run the application, you will get :

Thank you for reading the first part of the recipe app tutorials. Please give me feedback. I’m always open to new ideas, and your opinions are so valuable to me!

Semir Umut Kurt

Written by

Junior Software Developer

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade