Understanding Spring Boot Architecture

M S Sandeep Kamath
Level Up Coding
Published in
6 min readJan 29, 2023

--

The content in the article aids in understanding Java Spring Boot Framework’s layers.

Instead of heading for a big breakdown, I decided to have a small breakdown every Tuesday evening.” ― Graham Parke

The best way to examine any software is to separate it into layers, then merge those layers back together, So Lets follow the same here.

Before diving into Java Spring Boot, let’s have a look at a well-known example the OSI model in computer network. Although networking as a whole appears to be complex, we often divide it into layers in order to organize the protocols. We also state that each layer relies on the services offered by the layer below it. In Spring boot, the same principle applies.

Spring Boot Layers:

Self made image

We can mainly divide Spring boot into 4 layers,

  1. Controller Layer

The first part of the system that engages with the client request is a Controllers. They define the endpoints of the API, one can imagine end-points as valid routes and request method(GET,POST,PUT). Controller’s main goal is to offer services to the client i.e. providing the response, status and more. Controller makes use of the services provided by Service layer in order to serve the client.

Example of Endpoints:

/login (POST)
/register (POST)
/products (GET)

2. Service layer

Service layers are meant to implement business logics. The main aim of service layer is to offer services to controller layer. All sort of computations on the data are performed in this layer, thus service layer requires data. Therefore they rely on the services offered by the DAO/Repository layer.

3. Repository/DAO layer

DAO stands for Data Access Object, The main goal of this layer is access(Query) data efficiently from database and provide services to the Service layer.

Source: GeeksForGeeks

There exists interfaces in spring boot which provides us CRUD operations (CREATE, RETRIEVE, UPDATE, DELETE). Thus Repository layer can implement one of these. Its worth to have a look at different functionality provided by there repositories from here.

4. Model:

Model represents real world objects. Thus these objects are referred to as Models. JPA (Java Persistence API) provides reference or details about ORM( Object Relation Mapping) which means the Java class can we related to the database table. There exists many implementation of JPA ORM, one of them is Hibernate. Therefore you will require the Java class of real world entities and then map it to the relation(Table).

Template for implementation of the above Layers:

Note: For the implementations, lets consider Project Management as a topic.

Controller Layer:

ProjectController.Java

package com.example.Controller;
//import statements goes here
@RestController
public class UserController {

//List all the available projects
@GetMapping(path = "/projects", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<List<Project>> getProjects() {

// perform validation checks
// return the services provided by service layer
}
//Apply for the project
@PostMapping(path = "/apply-project", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<HttpStatus> applyProject(@RequestBody Map<String,String> json) {
// perform validation checks
// return the services provided by service layer
}

//Upload resume
@PostMapping(path = "/upload-resume/{usn}")
public ResponseEntity<List<Object>> uploadToDB(@RequestParam("file") MultipartFile[] file,@PathVariable String usn) {

// perform validation checks
// return the services provided by service layer
}
//Download resume
@GetMapping("/files/download/{fileName:.+}")
public ResponseEntity downloadFromDB(@PathVariable String fileName) {
// perform validation checks
// return the services provided by service layer
}
}

The above example makes use of @ annotations, These are used to inform spring whether it is a RestController, PostMapping etc.

Service Layer:

ProjectService.Java

package com.example.Service;

// import statements

public interface ProjectService {

ResponseEntity<List<Project>> getProjects();

HttpStatus applyProject(String USN,int project_id);

ResponseEntity<List<Object>> uploadProjectDocument(MultipartFile[] files,int project_id);

}

ProjectServiceImpl.Java

package com.example.Service;

//import statements
@Service
public class ProjectServiceImpl implements ProjectService {
//dependency injection of DAO to be gone here (Autowire)

@Override
public ResponseEntity<List<Project>> getProjects() {
try {
//Business logic implementation using DAO services
} catch (Exception e) {
return new ResponseEntity<>(null,HttpStatus.INTERNAL_SERVER_ERROR) ;
}
}

@Override
public HttpStatus applyProject(String USN, int project_id) {

//Business logic implementation using DAO services
}

//helper functions
public ResponseEntity uploadToLocalFileSystem(MultipartFile file,int project_id) {

}
@Override
public ResponseEntity<List<Object>> uploadProjectDocument(MultipartFile[] files,int project_id) {
//Business logic implementation using DAO services
}

}

Repository/DAO Layer:

ProjectDAO.java

package com.example.Dao;

//import statements

public interface ProjectDao extends JpaRepository<Project,Integer> {

//You can also include native queries on top of CRUD operations provided by JPA
// Add queries here using @Query annotations and corresponding functions

@Query(value = "Your SQL query ",nativeQuery = true)
public List<Project> getProjects();

}

}

Model:

Project.java

package com.example.Entity;

//import statements

@Entity
@Table(name = "project")
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int project_id;
@Column(nullable = false, name = "company_name")
private String company_name;

@Column(nullable = false, name = "description")
private String description;

@Column(nullable = false, name = "requirements")
private String requirements;

@Column(nullable = false, name = "manager")
private String manager;
@Column(nullable = false, name = "start_date")
private Date start_date = new Date();

@Column( name = "end_date")
private Date end_date = new Date();
@Column(nullable = false,name = "opening")
private int opening;
@Column(name = "resources")
private String resources;
public Set<Staff> getStaff_incharge() {
return staff_incharge;
}
public void setStaff_incharge(Set<Staff> staff_incharge) {
this.staff_incharge = staff_incharge;
}
public Set<Student> getApplied_students() {
return applied_students;
}
public Set<Document> getDocuments() {
return documents;
}
public void setDocuments(Set<Document> documents) {
this.documents = documents;
}
@JsonIgnore
@ManyToMany(mappedBy="funded_projects")
private Set<Fund> funds;
public Set<Fund> getFunds() {
return funds;
}
public void setFunds(Set<Fund> funds) {
this.funds = funds;
}
public void setApplied_students(Set<Student> applied_students) {
this.applied_students = applied_students;
}
public Set<Student> getWorking_students() {
return working_students;
}
public void setWorking_students(Set<Student> working_students) {
this.working_students = working_students;
}
//constructors
public Project() {
super();
}
public Project(int project_id, String company_name, String description, String requirements, String manager, Date start_date, Date end_date, int opening, String resources) {
super();
this.project_id = project_id;
this.company_name = company_name;
this.description = description;
this.requirements = requirements;
this.manager = manager;
this.start_date = start_date;
this.end_date = end_date;
this.opening = opening;
this.resources = resources;
}
public int getProject_id() {
return project_id;
}
public void setProject_id(int project_id) {
this.project_id = project_id;
}
public String getCompany_name() {
return company_name;
}
public void setCompany_name(String company_name) {
this.company_name = company_name;
}

public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getRequirements() {
return requirements;
}
public void setRequirements(String requirements) {
this.requirements = requirements;
}
public String getManager() {
return manager;
}
public void setManager(String manager) {
this.manager = manager;
}
public Date getStart_date() {
return start_date;
}
public void setStart_date(Date start_date) {
this.start_date = start_date;
}
public Date getEnd_date() {
return end_date;
}
public void setEnd_date(Date end_date) {
this.end_date = end_date;
}
public int getOpening() {
return opening;
}
public void setOpening(int opening) {
this.opening = opening;
}
public String getResources() {
return resources;
}
public void setResources(String resources) {
this.resources = resources;
}
@Override
public String toString() {
return "Project{" +
"project_id=" + project_id +
", company_name='" + company_name + '\'' +
", description='" + description + '\'' +
", requirements='" + requirements + '\'' +
", manager='" + manager + '\'' +
", start_date=" + start_date +
", end_date=" + end_date +
", opening=" + opening +
", resources='" + resources + '\'' +
'}';
}
}

There is a wide use of annotations, have a look at it from here. In the above example, The class represents a table and its data members represents the attributes of the table. We can also represent the relationship between tables using OnetoOne, ManyToOne, ManyToMany annotations. Learn more about ORM here.

Learn more about implementation from https://www.baeldung.com/spring-boot

The above implementation was incomplete because the aim of the article is to understand the workflow and layers which, has nothing to do with implementation. Spring boot is huge and I have covered only a very small part of it. My sincere apologies for any errors in the article, and I hope that helpful, Thank you.

--

--

Cheerful techie with a passion for Android development, building backends. Let's geek out together! 🚀😄