When I first stepped into backend development, I believed programming was only about one thing:
“If the output comes, the job is done.”
I bundled everything into one giant file — controllers, logic, database access.I didn’t understand why Spring insisted on layers or why DI, IoC, and annotations were so important. The project structure looked overwhelming, and everything felt abstract.
Everything changed the day I decided to trace my first Spring Boot request and watch how a simple API call traveled through the system. Suddenly, what looked like scattered pieces turned into a well-designed, meaningful architecture. It didn’t just teach me Spring Boot — it changed the way I think about writing software.
What I Used to Think Backend Development Was
In the beginning, this was my reality:
- All classes dumped into one package
- Endless, congested code inside one file
- No idea what executes, what doesn’t, or why
- Maven structure felt unnecessary
- Layers didn’t make sense
- Annotations were intimidating
- No understanding of why Service existed between Controller and Repository
It was like trying to build a house without knowing what bricks, beams, or foundations were.
The Turning Point: Tracing My First Request
One day, I opened my console logs and started following the journey of an incoming request:
- It first hit the Controller
- Then passed into the Service
- Then reached the Repository
- Finally touched the Database
- And then returned the response through the same layers
That visual flow — even just in the logs — changed everything for me.
I realized:
- DI reduces tight coupling
- IoC manages object lifecycles so you don’t
- Layers exist to protect and organize the system
- Spring Boot is powerful because of its structure, not despite it
Architecture became a living system, not a theory.
Controller → Service → Repository: The Flow That Made Everything Click
Controller — The Entry Point
The controller receives the request, interacts with the service, and returns the appropriate response or view.
@Controller
@RequestMapping("/employees")
public class EmployeeController {
private EmployeeService employeeService;
public EmployeeController(EmployeeService theEmployeeService) {
employeeService = theEmployeeService;
}
@GetMapping("/list")
public String listEmployees(Model theModel) {
List<Employee> theEmployees = employeeService.findAll();
theModel.addAttribute("employees", theEmployees);
return "employees/list-employees";
}
}
Controllers should be small and focused — no business logic inside them.
Service — The Logic Layer
This is where business decisions happen. It protects the repository from being accessed directly.
public interface EmployeeService {
List<Employee> findAll();
Employee findById(int theId);
void save(Employee theEmployee);
void deleteById(int theId);
boolean existsByEmailId(Employee theEmployee);
}
Implementation:
@Service
public class EmployeeServiceImpl implements EmployeeService {
private EmployeeRepository employeeRepository;
@Autowired
public EmployeeServiceImpl(EmployeeRepository theEmployeeRepository) {
employeeRepository = theEmployeeRepository;
}
@Override
public List<Employee> findAll() {
return employeeRepository.findAllByOrderByLastNameAsc();
}
}
The service layer keeps your application clean, secure, and maintainable.
Repository — Where Database Interactions Happen
With Spring Data JPA, repositories become incredibly simple:
public interface EmployeeRepository extends JpaRepository<Employee, Integer> {
boolean existsByEmail(String email);
List<Employee> findAllByOrderByLastNameAsc();
}
The framework generates most queries automatically.
Entity — Mapping Java Objects to Database Tables
Entities act as a bridge between the database and your Java application.
@Entity
@Table(name="employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@NotBlank
private String firstName;
@NotBlank
private String lastName;
@NotBlank
private String email;
// constructors, getters, setters
}
Spring (plus Jackson) takes care of converting between JSON ↔ Java ↔ Database.
When MVC Finally Made Sense
Moving into full MVC with Thymeleaf gave me clarity:
- Controllers handle the request
- Services handle business logic
- Repositories handle persistence
- Templates display the data
The project structure stopped looking like a burden and became something I respected. The flow felt natural and powerful.
A Simple CRUD Example That Made It Real
The moment everything clicked was when I saw this flow in my own CRUD app:
- Client hits GET /employees/list
- Controller asks Service for data
- Service queries the Repository
- Repository returns data from the database
- Controller adds data to Model
- Thymeleaf displays it on the page
Suddenly the architecture wasn’t an academic concept — it was working in front of me.
Here is the GitHub repository of the CRUD app I used in this article:
👉 **https://github.com/MAffanG/employee-crud-api**
What This Taught Me About Coding
Understanding the request flow transformed everything about my approach:
- I no longer chase outputs — I chase clarity
- I think in terms of architecture, not hacks
- Debugging became easier and faster
- My code became cleaner and more intentional
- I finally felt like a backend developer, not someone just trying things until they work
This experience didn’t just teach me Spring Boot — it changed the way I respect software design as a whole.
