Building the Spring Boot Application

Creating a Spring Boot Application

ReadingPreview

আপনি একটি free preview lesson দেখছেন।

এখন পর্যন্ত আমরা intentionally code লেখা শুরু করিনি।

আমরা প্রথমে বুঝেছি:

Product Requirement
        ↓
Acceptance Criteria
        ↓
Engineering Context
        ↓
Backlog
        ↓
Technical Design
        ↓
RFC
        ↓
ADR
        ↓
Design Review

এখন design sufficiently clear।

তাই এবার implementation শুরু করার সময়।

আমাদের প্রথম ticket:

BACKEND-101
Bootstrap Order Management Backend

এই lesson-এ আমরা একটি minimal কিন্তু production-oriented Spring Boot application তৈরি করব।

Goal হলো যত দ্রুত সম্ভব অনেক dependencies বা folders তৈরি করা নয়।

Goal:

একটি clean, reproducible Java application foundation তৈরি করা যার উপর পুরো Order Management Backend evolve করবে।


Ticket: BACKEND-101

আমাদের initial backlog অনুযায়ী:

# BACKEND-101 — Bootstrap Order Management Backend

## Context

আমাদের backend implementation-এর জন্য initial Spring Boot application প্রয়োজন।

## Scope

- Create the Gradle project.
- Configure Spring Boot.
- Establish the application entry point.
- Add baseline project configuration.
- Verify the application can build and start locally.

## Acceptance Criteria

- The project builds successfully with Gradle.
- The Spring Boot application starts successfully.
- The repository contains the agreed baseline project structure.
- A clean checkout can be built without IDE-specific setup.

## Out of Scope

- Product APIs
- Database schema
- Authentication
- Order logic
- Payment integration
- Production Docker setup

এই scope আমাদের implementation guide করবে।


Start With the Ticket, Not Spring Initializr

Spring Boot project তৈরি করতে technically কয়েক মিনিটই লাগে।

কিন্তু একজন backend engineer-এর প্রশ্ন হওয়া উচিত না:

কোন checkbox select করব?

প্রথম প্রশ্ন:

এই ticket complete করার জন্য minimum কী দরকার?

বর্তমানে দরকার:

Java project
Gradle build
Spring Boot runtime
application entry point
basic configuration
repeatable build

এই মুহূর্তে দরকার নেই:

PostgreSQL
JPA
Flyway
Spring Security
WebClient
Testcontainers
Payment SDK

সেগুলো later tickets/modules-এ আসবে যখন requirement থাকবে।


Avoid Dependency Shopping

নতুন Spring Boot project শুরু করার সময় common mistake:

Spring Web
Spring Data JPA
PostgreSQL
Security
Redis
Kafka
Actuator
Validation
WebFlux
Lombok
...

সব একসাথে add করা।

Reason:

"Eventually লাগবে।"

এই approach-এর problem:

larger dependency surface

more configuration

more startup behaviour

more things to understand

more accidental architecture

আমাদের principle:

Add a dependency when a current engineering requirement needs it.


Initial Technology Choices

Engineering context already established:

Language:
Java

Framework:
Spring Boot

Build Tool:
Gradle

তাই এগুলো নিয়ে নতুন comparison দরকার নেই।

আমরা project foundation তৈরি করব এই assumptions-এর উপর।


Repository Name

আমাদের project:

order-management

Repository root conceptually:

order-management/
├── build.gradle
├── settings.gradle
├── gradlew
├── gradlew.bat
├── gradle/
└── src/

Later repository evolve হবে:

order-management/
├── src/
├── docs/
│   ├── rfcs/
│   ├── adr/
│   └── incidents/
├── docker/
├── compose.yaml
├── Dockerfile
└── README.md

কিন্তু সব file এখনই দরকার নেই।


Why Use the Gradle Wrapper?

একটি Gradle project-এ আমরা সাধারণত run করি:

./gradlew build

instead of requiring every engineer to install a particular Gradle version globally।

Wrapper files:

gradlew
gradlew.bat
gradle/wrapper/

project-এর সঙ্গে থাকে।

Benefit:

developer machine
CI
another engineer

সবাই project-defined Gradle version ব্যবহার করতে পারে।

This supports our acceptance criterion:

A clean checkout can be built without IDE-specific setup.


Build Reproducibility Matters

Weak setup:

Works from IntelliJ on my machine.

Strong setup:

git clone
./gradlew build

works।

IDE convenience useful।

কিন্তু IDE project-এর build system নয়।

Our source repository must remain the source of truth।


Initial Gradle Project

A minimal settings.gradle:

rootProject.name = 'order-management'

এর কাজ simple:

Gradle project name define করা

এখানে application architecture configure করার দরকার নেই।


Initial build.gradle

A minimal Spring Boot Gradle build conceptually:

plugins {
    id 'java'
    id 'org.springframework.boot' version '<spring-boot-version>'
    id 'io.spring.dependency-management' version '<dependency-management-version>'
}

group = 'io.liveklass'
version = '0.0.1-SNAPSHOT'

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'

    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

tasks.named('test') {
    useJUnitPlatform()
}

এখানে version placeholder intentionally দেখানো হয়েছে।

Actual project bootstrap-এর সময় team-supported current versions project build-এর মধ্যে pin করা হবে।

Important হলো structure বুঝতে পারা।


Why Java Toolchains?

এই block:

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

build-এর expected Java language/runtime toolchain clearly declare করে।

এর benefit:

developer environment consistency

CI consistency

explicit project requirement

Without this, build accidentally developer machine-এর default Java version-এর উপর depend করতে পারে।


Why Not "Whatever Java Is Installed"?

Imagine:

Engineer A → Java 21

Engineer B → Java 25

CI → another version

Then subtle differences appear।

A production project should state its supported Java baseline explicitly।


Why Only spring-boot-starter?

At this exact stage, আমাদের requirement শুধু Spring Boot application start করা।

Therefore:

implementation 'org.springframework.boot:spring-boot-starter'

is enough conceptually।

We are not yet exposing REST endpoints।

Later REST module needs:

Spring Web

Persistence module needs:

Spring Data JPA
PostgreSQL driver
Flyway

Security module needs:

Spring Security

Each dependency should arrive with a reason।


Test Dependency

We include:

testImplementation 'org.springframework.boot:spring-boot-starter-test'

because testing is already part of our Definition of Done।

Even bootstrap work should be buildable and testable।

We are not postponing testing until Module 10।

Module 10 will deepen testing strategy।


Project Source Structure

Standard Java structure:

src/
├── main/
│   ├── java/
│   └── resources/
└── test/
    └── java/

Meaning:

src/main/java
→ application source code

src/main/resources
→ application resources/configuration

src/test/java
→ automated tests

Spring Boot works naturally with this structure।


Base Package

Suppose our package root is:

io.liveklass.ordermanagement

Then:

src/main/java/io/liveklass/ordermanagement/

contains the application।

Why use a proper package root?

Because Java package names provide:

namespace

code organization

component scanning boundary

Later capabilities can live below it:

io.liveklass.ordermanagement.product

io.liveklass.ordermanagement.inventory

io.liveklass.ordermanagement.order

io.liveklass.ordermanagement.payment

io.liveklass.ordermanagement.security

We don't need to create all those packages yet।


Why the Main Class Belongs at the Root

Our application entry point can live at:

io.liveklass.ordermanagement.OrderManagementApplication

This is useful because Spring Boot's default component scanning starts from the package containing the main application class and includes subpackages।

Conceptually:

io.liveklass.ordermanagement
├── OrderManagementApplication
├── product
├── inventory
└── order

is a natural structure।


Application Entry Point

Our main class:

package io.liveklass.ordermanagement;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class OrderManagementApplication {

    public static void main(String[] args) {
        SpringApplication.run(
                OrderManagementApplication.class,
                args
        );
    }
}

This is the entry point for the application।


What Does main Do?

Normal Java program starts from:

public static void main(String[] args)

Spring Boot does not change that fundamental Java rule।

We still have a normal Java entry point।

Inside it:

SpringApplication.run(...)

bootstraps Spring Boot।

Conceptually:

JVM starts
    ↓
main(...)
    ↓
SpringApplication.run(...)
    ↓
Spring Application Context created
    ↓
configured application starts

@SpringBootApplication

This annotation:

@SpringBootApplication

is central to Spring Boot startup।

For now think of it as the annotation that marks our primary Spring Boot application configuration।

It enables Spring Boot's core startup conventions, including configuration and component discovery behaviour।

We will unpack Beans এবং Application Context properly in upcoming lessons।

For this lesson, important point:

It tells Spring Boot where our application starts.


Don't Memorize the Annotation Yet

A beginner often sees:

@SpringBootApplication

and thinks:

I need to memorize this annotation.

Better question:

What responsibility is it fulfilling?

Answer:

bootstrap/configuration entry point

Later যখন আমরা application context বুঝব, annotation-এর behaviour much clearer হবে।


Application Configuration File

Inside:

src/main/resources/

we can create:

application.yml

Initial file can remain minimal:

spring:
  application:
    name: order-management

This gives the Spring application an explicit name।

We do not need to fill configuration with future database/payment/security properties yet।


Why YAML?

Spring Boot supports multiple property formats।

Our project can choose:

application.yml

for readable structured configuration।

This is a project convention, not a deep architecture decision।

Consistency matters more than arguing YAML vs .properties for every project।


Don't Put Secrets Here

Even at bootstrap stage, establish the correct habit:

Bad:

payment:
  api-key: super-secret-key

committed into Git।

Later external credentials should come from appropriate external configuration/environment mechanisms।

We'll cover this properly in production-readiness lessons।


Initial Project Structure

After bootstrap:

order-management/
├── build.gradle
├── settings.gradle
├── gradlew
├── gradlew.bat
├── gradle/
│   └── wrapper/
│
└── src/
    ├── main/
    │   ├── java/
    │   │   └── io/
    │   │       └── liveklass/
    │   │           └── ordermanagement/
    │   │               └── OrderManagementApplication.java
    │   │
    │   └── resources/
    │       └── application.yml
    │
    └── test/
        └── java/
            └── io/
                └── liveklass/
                    └── ordermanagement/

এটাই যথেষ্ট initial foundation।


Where Are Product and Order Packages?

এখনও নেই।

Why?

Because:

BACKEND-101

does not implement Product or Order।

Architecture design says those capabilities will exist।

But empty folders/classes create করলে কোনো meaningful implementation হয় না।

We'll create them when the corresponding work begins।


Avoid Architecture by Empty Folder

Bad bootstrap:

product/
    controller/
    service/
    repository/
    entity/
    dto/
    mapper/

order/
    controller/
    service/
    repository/
    entity/
    dto/
    mapper/

সব empty।

এটি architecture নয়।

এটি speculative folder generation।

Good architecture becomes visible as actual responsibilities arrive।


First Build

Now run:

./gradlew build

What should happen conceptually?

Gradle:

reads build.gradle
    ↓
resolves dependencies
    ↓
compiles main source
    ↓
compiles test source
    ↓
runs tests
    ↓
packages application

If successful:

BUILD SUCCESSFUL

This satisfies an important part of BACKEND-101।


Why Build Before Running?

Because application running from IDE can hide build problems।

Running:

./gradlew build

first verifies:

project definition
dependency resolution
compilation
tests
packaging

We want the repository build to be healthy before development continues।


Running the Application

Run:

./gradlew bootRun

Spring Boot starts the application।

Conceptually you'll see startup logs indicating application initialization।

At this stage we don't necessarily have an HTTP server because we haven't added the web starter yet।

That is okay।

Our current acceptance criterion says:

Spring Boot application starts successfully.

It does not say:

GET /products works.

That is future scope।


Another Way to Run the Application

After:

./gradlew build

Spring Boot produces an executable application artifact under:

build/libs/

It can be run using Java:

java -jar build/libs/<application-jar>.jar

This is valuable because it verifies the application runs outside IDE tooling।


IDE Is Optional to the Build

You can use:

IntelliJ IDEA
VS Code
another Java IDE

for development।

But our workflow should remain:

Repository
+
Gradle
=
authoritative build

not:

IDE project configuration
=
authoritative build

This matters enormously in CI later।


First Test

A standard Spring Boot bootstrap test may look like:

package io.liveklass.ordermanagement;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class OrderManagementApplicationTests {

    @Test
    void contextLoads() {
    }
}

The test appears almost empty।

But its purpose is specific:

Can Spring create the application context successfully?

What Does contextLoads() Actually Protect?

Suppose later configuration becomes invalid:

missing required bean

invalid configuration

broken dependency wiring

A context startup test may catch some classes of application bootstrap failure।

At the bootstrap stage, this is reasonable coverage।


But Don't Overvalue contextLoads()

Passing:

contextLoads()

does not mean:

business logic correct

database correct

API correct

security correct

It only provides startup-level confidence।

Testing should always correspond to behaviour being protected।


Testing Starts With the Work

For BACKEND-101, relevant behaviours are:

project compiles

Spring context can start

build succeeds

Therefore bootstrap test is appropriate।

Later Product behaviour gets Product tests।

Order behaviour gets Order tests।

We do not write fake business tests before those features exist।


Build Artifact

After a successful build:

build/

contains generated outputs।

This directory should not be committed into Git।

Why?

Because it is reproducible build output।

Source repository should contain:

source
build definition
configuration
migrations
documentation

not generated build artifacts।


.gitignore

A basic .gitignore should account for generated/local files।

For example:

.gradle/
build/

.idea/
*.iml

.vscode/

.DS_Store

Exact IDE entries depend on team conventions।

Important principle:

Don't commit machine-generated local state unless the repository intentionally owns it.


Gradle Wrapper Is Different

Do not ignore:

gradlew

gradlew.bat

gradle/wrapper/

These are intentionally committed because they are part of reproducible project tooling।

Distinguish:

generated build output

from:

versioned build tooling

README at Bootstrap Stage

A lightweight README.md can help another engineer start the project।

Example:

# Order Management Backend

Backend service for the Commerce Order Management project.

## Requirements

- Supported Java version
- Docker later when local infrastructure is introduced

## Build

```bash
./gradlew build

Run

./gradlew bootRun

README should describe what actually works now।

Don't document fictional future commands।

---

# Clean Checkout Test

One of our acceptance criteria:

> A clean checkout can be built without IDE-specific setup.

How do we validate this?

Conceptually:

```text
fresh repository checkout
        ↓
supported Java available
        ↓
./gradlew build

should succeed।

This is a powerful test of project bootstrap quality।


Hidden Local Dependencies

Imagine build works only because your machine has:

manually installed library

IDE-specific generated source

custom local environment variable

uncommitted configuration

Then project is not reproducible।

BACKEND-101 exists partly to prevent this problem before the codebase grows।


Should We Add PostgreSQL Now?

No।

That's:

BACKEND-102
Configure PostgreSQL and Flyway

If we add PostgreSQL here:

  • BACKEND-101 scope expands
  • first application startup now depends on database
  • we mix two separate engineering concerns

We intentionally keep tickets bounded।


Should We Add Spring Web Now?

Our immediate project eventually exposes REST APIs।

Could we add Spring Web early?

Technically yes।

But current ticket doesn't need HTTP behaviour yet।

The course plan will introduce Spring Boot structure, DI, Beans, configuration, and later REST APIs deliberately।

We can introduce web dependency when we begin the HTTP application boundary rather than treating dependencies as future placeholders।


Should We Add Actuator Now?

Not yet।

Actuator belongs to production readiness later।

Adding it now without using or explaining it gives the learner a dependency without context।

We'll add it when health checks and metrics become actual requirements।


Should We Add Lombok?

No current requirement demands it।

Java records, constructors, IDE generation, and normal language features already give us options।

Introducing Lombok would add an additional compile-time dependency and learning surface।

We don't need it for bootstrap।

This is not a statement that Lombok is universally bad।

It simply has no current justification।


Should We Add MapStruct?

No।

We don't even have DTO/domain mapping complexity yet।

Don't install a solution before we have the problem।


Build Configuration Is Production Code

Developers sometimes think:

src/main/java
=
real code

build.gradle
=
boring setup

But broken build configuration can stop the entire engineering team।

build.gradle determines:

dependencies

Java version

test execution

packaging

plugins

It is part of the product's engineering system।

Treat changes to it carefully।


Dependency Versions Must Be Controlled

One developer should not randomly update framework versions because:

newer is available

Dependency changes can affect:

runtime behaviour

security fixes

compatibility

build behaviour

In a real team, significant upgrades are explicit engineering changes।

For this course, dependencies will evolve only when required by the roadmap।


Package Naming Matters, but Don't Overthink It

Our base package should be:

io.liveklass.ordermanagement

or another agreed organization-specific namespace।

We need consistency।

We don't need architecture meetings about:

ordermanagement
vs
order_management
vs
orderservice

once a clear convention is chosen।

Spend design effort on important decisions।


Application Name

Configuration:

spring:
  application:
    name: order-management

is useful because application identity later appears in operational contexts such as:

logs
metrics
configuration
deployment

Naming consistently from the start avoids unnecessary drift।


0.0.1-SNAPSHOT

A Gradle version like:

version = '0.0.1-SNAPSHOT'

simply indicates an early development artifact version।

We are not designing a full release-versioning policy in this lesson।

Version strategy belongs to delivery/release process later।


What Have We Actually Built?

At this point, very little business functionality exists।

That is intentional।

We have:

a valid Java project

a Gradle build

a Spring Boot runtime

an application entry point

baseline configuration

a bootstrap test

reproducible commands

This is foundation, not feature delivery।


Why This Is Still Meaningful Engineering Work

Every future ticket depends on a healthy application foundation।

If bootstrap is chaotic:

different Java versions

IDE-only builds

uncontrolled dependencies

random package structure

every feature pays that cost।

BACKEND-101 creates a stable starting point।


Definition of Done Review

Now apply our project Definition of Done।


Scope

Did we implement agreed scope?

[✓] Gradle project exists

[✓] Spring Boot configured

[✓] Application entry point exists

[✓] Baseline configuration exists

[✓] Application builds and starts

Acceptance Criteria

[✓] ./gradlew build succeeds

[✓] Spring Boot application starts

[✓] Baseline project structure exists

[✓] Build does not depend on IDE-specific setup

Testing

Relevant automated test:

[✓] Spring application context startup test

Existing tests:

[✓] pass

Build

[✓] clean Gradle build succeeds

Scope Discipline

Did we add:

Product API?
Database?
Security?
Payment?
Docker production setup?

No।

Good।


Example Pull Request

Our team workflow says implementation should eventually go through a Pull Request।

A reasonable PR description:

## What

Bootstraps the Order Management Backend as a Spring Boot Gradle application.

## Why

Provides the application foundation required for subsequent backend capabilities.

## Changes

- Added Gradle project and wrapper
- Added Spring Boot application entry point
- Added baseline application configuration
- Added application-context bootstrap test
- Added basic project documentation

## Testing

- `./gradlew build`
- Application started with `./gradlew bootRun`

## Risks

Low. This change introduces the initial application foundation and no business functionality.

This is a good example of a small, reviewable first PR।


What Should the Reviewer Check?

Reviewer should not spend most of the review debating whitespace।

Important questions:

Does clean build work?

Is the Java baseline explicit?

Are unnecessary dependencies included?

Is the package root reasonable?

Can the application start outside the IDE?

Is configuration minimal and safe?

Does the change stay within BACKEND-101 scope?

This keeps review aligned with the ticket।


Example Review Feedback

Reviewer:

PostgreSQL dependency is included, but this ticket does not configure or use a database. Can we add it with BACKEND-102 instead?

Good feedback।

Response:

Yes.
Remove the unused dependency.

This improves dependency discipline।


Another Review Comment

Reviewer:

There are empty product, inventory, order, and payment package hierarchies. Nothing uses them yet.

Again useful।

Response:

Remove them.
Create capability structure as implementation reaches each capability.

Architecture exists in our design docs।

Empty folders are not required to prove it।


Keep the Main Branch Buildable

After BACKEND-101 merges, main branch should be in a state where:

./gradlew build

passes।

This will become an ongoing expectation।

Every future change starts from a healthy baseline and should leave one behind।


Small PR, Important Foundation

Our first implementation PR is intentionally small।

That gives us:

easy review

clear ownership

simple rollback

clean project history

Future Git history may look like:

BACKEND-101 Bootstrap Order Management Backend

BACKEND-102 Configure PostgreSQL and Flyway

BACKEND-103 Model Product Domain
...

This tells a coherent engineering story।


Spring Boot Is Now a Tool, Not the Course Goal

Notice how Spring Boot entered the course।

We did not begin with:

Today we will learn @SpringBootApplication.

We began with:

We need to bootstrap the agreed backend application.

Then Spring Boot solved that need।

This is exactly the mindset we want throughout the course:

Problem first. Technology second.


What We Have Not Learned Yet

There are important Spring concepts we have only touched lightly:

project structure

dependency injection

beans

application context

configuration

These are upcoming lessons।

We don't need to fully explain every Spring mechanism in the first implementation lesson।

Learning will follow the system as it evolves।


Where We Are Now

Module 3:

Creating a Spring Boot Application
        ↓
Understanding Spring Boot Project Structure
        ↓
Dependency Injection
        ↓
Beans and the Application Context
        ↓
Configuration and Application Properties
        ↓
Environment-Specific Configuration
        ↓
Structuring a Backend Codebase
        ↓
Running the Application Locally

BACKEND-101 will evolve through these lessons।

This lesson established the executable foundation।


Engineering Principle

The core principle:

A project bootstrap should establish the minimum reproducible foundation required for the next piece of meaningful work.

Another:

Dependencies, packages, and infrastructure should be added because the current system needs them—not because we expect every backend might eventually use them.

And:

If the project only works through one developer's IDE, the project is not properly bootstrapped.


Summary

In this lesson, we established that:

  • BACKEND-101 is the first implementation ticket.
  • The initial application is a Gradle-based Spring Boot project.
  • The repository build, not the IDE, is the source of truth.
  • Gradle Wrapper makes the project build reproducibly across environments.
  • The Java baseline should be explicit.
  • The Spring Boot main class provides the application entry point.
  • @SpringBootApplication marks the primary application bootstrap configuration.
  • SpringApplication.run(...) starts the Spring application.
  • Initial configuration should remain minimal.
  • Secrets must not be committed into application configuration.
  • We should not pre-install PostgreSQL, Security, Redis, Kafka, Actuator, or other dependencies before their requirements arrive.
  • Empty package hierarchies do not make an architecture.
  • ./gradlew build should succeed from a clean checkout.
  • ./gradlew bootRun provides a build-system-driven way to start the application.
  • A basic application-context test is appropriate for the bootstrap ticket.
  • Testing begins with implementation rather than being postponed to a later module.
  • Build configuration is part of production engineering, not incidental setup.
  • BACKEND-101 should result in a small, focused, reviewable Pull Request.
  • The main branch should remain buildable after the bootstrap change.

Next lesson:

Understanding Spring Boot Project Structure

There we will examine the project we just created and understand what each part of the repository actually does, how Java package placement affects Spring Boot behaviour, where configuration/resources/tests belong, and how to avoid turning a growing backend into an unstructured collection of files.