Building the Spring Boot Application

Understanding Spring Boot Project Structure

ReadingPreview

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

আগের lesson-এ আমরা BACKEND-101 — Bootstrap Order Management Backend শুরু করেছি এবং একটি minimal Spring Boot application foundation তৈরি করেছি।

Initial structure ছিল:

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/

দেখতে simple।

কিন্তু এই structure-এর প্রতিটি অংশের specific responsibility আছে।

এই lesson-এর goal:

Project structure memorise করা নয়; কোন code কোথায় থাকে এবং কেন থাকে সেটা বোঝা।


Project Structure Is Part of Architecture

একটি backend project grow করলে dozens বা hundreds of classes হতে পারে।

যদি files random জায়গায় রাখা হয়:

OrderService.java

Utils.java

Controller2.java

Helper.java

Manager.java

DatabaseThing.java

তাহলে engineer-এর জন্য codebase-এর mental model তৈরি করা কঠিন হয়ে যায়।

একটি useful project structure answer করে:

Where does application code live?

Where does configuration live?

Where do tests live?

Where does build configuration live?

Where should a new capability be added?

Which files belong to the framework,
and which files represent our business domain?

Structure কোনো cosmetic concern নয়।

It affects:

discoverability

dependency direction

maintainability

Spring component scanning

testing

code review

Repository Root

Let's begin from the top।

order-management/

এই directory পুরো repository represent করে।

এর মধ্যে থাকবে:

source code

build configuration

documentation

database migrations

container configuration

CI configuration

as the project evolves।

Repository root should make it possible for a new engineer to understand:

How do I build this?

How do I run this?

Where is the application?

Where is the documentation?

settings.gradle

File:

settings.gradle

At the moment:

rootProject.name = 'order-management'

এর responsibility:

define the Gradle project identity

Simple project-এ এর content খুব ছোট হতে পারে।

Later multi-project Gradle build হলে এখান থেকে subprojects include করা সম্ভব।

কিন্তু আমাদের current application:

single Gradle project

So no need to introduce Gradle submodules।


Gradle Subprojects Are Not Domain Modules

এটি একটি important distinction।

আমরা architecture-এ বলেছি:

Product
Inventory
Order
Payment

logical capabilities।

তার মানে এই নয় যে আমাদের তৈরি করতে হবে:

:product
:inventory
:order
:payment

Gradle subprojects।

Domain/module boundary এবং build module একই জিনিস নয়।

Current system simple enough যে:

one Gradle project
+
capability-oriented Java packages

sufficient।


build.gradle

build.gradle defines how the application is built।

It typically contains:

plugins

Java version

repositories

dependencies

test configuration

build tasks

Example:

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

This tells Gradle which build capabilities are needed।


Build File Is Not Runtime Configuration

Important distinction:

build.gradle answers:

What does the project need to compile,
test, and package?

application.yml answers:

How should the application behave
when it runs?

Example:

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

belongs to build configuration।

Whereas:

spring:
  application:
    name: order-management

belongs to runtime application configuration।

Don't mix these concerns।


Gradle Wrapper

Files:

gradlew
gradlew.bat
gradle/wrapper/

form the Gradle Wrapper।

Typical commands:

./gradlew build
./gradlew test
./gradlew bootRun

The wrapper ensures project-defined Gradle tooling can be used consistently।

This matters for:

local development

CI

other engineers

fresh machines

src/main/java

This is where main Java application source lives।

src/main/java/

Java packages map to directory structure।

For:

package io.liveklass.ordermanagement;

file lives under:

src/main/java/
└── io/
    └── liveklass/
        └── ordermanagement/

The directories are not arbitrary।

They reflect Java package names।


Packages Are Namespaces

Suppose we have two classes:

io.liveklass.ordermanagement.order.Order

and:

io.liveklass.ordermanagement.api.Order

They technically can both be named Order because packages distinguish them।

But just because Java allows something does not mean it is a good naming decision।

Packages help:

group related concepts

avoid naming conflicts

express architecture

control visibility

support framework discovery

Base Package

Our root package:

io.liveklass.ordermanagement

This is the application namespace।

Later:

io.liveklass.ordermanagement.product

io.liveklass.ordermanagement.inventory

io.liveklass.ordermanagement.order

io.liveklass.ordermanagement.payment

io.liveklass.ordermanagement.security

can represent capability boundaries।


Why Reverse-Domain Package Naming?

Java projects often use names such as:

com.company.application

or:

io.liveklass.ordermanagement

because internet-domain-style naming helps create globally distinct namespaces।

For our course project:

io.liveklass.ordermanagement

is clear and consistent।

We do not need to spend more architecture effort on this naming convention।


The Application Class

Our entry point:

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 class should stay near the root package।

Why?

One reason is Spring's default component scanning behaviour।


Component Scanning

Spring needs to discover Spring-managed components।

For example, later we may have:

@RestController
class ProductController {
}
@Service
class ProductApplicationService {
}
@Repository
class ProductRepository {
}

Spring Boot normally scans packages beginning from the package containing the @SpringBootApplication class and its subpackages।

Our main class is:

io.liveklass.ordermanagement

So Spring can naturally discover:

io.liveklass.ordermanagement.product

io.liveklass.ordermanagement.inventory

io.liveklass.ordermanagement.order

because they are below that root।


Why Main Class Placement Matters

Suppose we incorrectly place the application class at:

io.liveklass.ordermanagement.bootstrap

Then default component scanning begins around that package।

Classes in:

io.liveklass.ordermanagement.order

are not children of:

io.liveklass.ordermanagement.bootstrap

So Spring may not discover them automatically।

You could configure scanning manually, but now you've created unnecessary configuration।

Better structure prevents the problem।


Prefer Structure Over Extra Configuration

You could solve component scanning using:

@ComponentScan(...)

with explicit packages।

But if normal package placement solves the problem naturally, additional configuration gives little value।

Principle:

Use Spring Boot conventions when they match the architecture instead of overriding them without a reason.


@SpringBootApplication Is More Than a Marker

Conceptually, @SpringBootApplication combines several Spring concerns।

At a high level it enables:

application configuration

auto-configuration

component scanning

We do not need to memorize its internal annotation composition right now।

Important is understanding why package placement underneath it matters।


src/main/resources

This directory stores non-Java resources that are packaged with the application।

src/main/resources/

For our project, initial content:

application.yml

Later it may contain:

db/migration/

logging configuration

static configuration resources

depending on application requirements।


Resources Are Included in the Application Artifact

When Gradle packages the application, resources under:

src/main/resources

become part of the application's classpath।

That is why Spring Boot can automatically find:

application.yml

and later Flyway can discover:

db/migration/

from its conventional location।


application.yml

Current:

spring:
  application:
    name: order-management

Later this file may contain safe default configuration such as:

application settings

database connection placeholders

integration configuration keys

But we should avoid turning it into an uncontrolled list of every environment's secrets।


Configuration Is Not Code Organization

Suppose payment provider URL exists।

Bad:

private static final String PAYMENT_URL =
        "https://provider.example.com";

inside business code।

Better architecture:

configuration
    ↓
integration component

But that comes later।

For now, remember:

src/main/java
→ behaviour/code

src/main/resources
→ resources/configuration

src/test/java

Automated test source lives here:

src/test/java/

Package structure commonly mirrors main source where appropriate।

Main class:

src/main/java/io/liveklass/ordermanagement/
    OrderManagementApplication.java

Test:

src/test/java/io/liveklass/ordermanagement/
    OrderManagementApplicationTests.java

This makes test location predictable।


Tests Belong Near Their Concept Semantically

Later:

order/domain/Order.java

may have:

order/domain/OrderTest.java

under test source।

Product workflow test may mirror:

product/application/

This helps engineers find tests associated with implementation।


Main Code Must Not Depend on Test Code

Dependency direction is:

test
  ↓
main application code

not:

main application code
  ↓
test utilities

Test source is excluded from production runtime artifact।

So production code should never rely on classes existing only under:

src/test/java

Test Resources

Later we may also use:

src/test/resources/

for test-specific resources।

For example:

test configuration

fixtures

sample files

if needed।

Again, don't create resources without a real test need।


What About src/main/webapp?

Traditional Java web applications sometimes use:

src/main/webapp

for WAR-based servlet deployment।

Our Spring Boot backend is an executable application and REST backend।

We do not need that structure।

Avoid copying conventions from unrelated Java project types।


Build Output

Gradle generates:

build/

This is not source।

It may contain:

compiled classes

test reports

generated resources

JAR artifacts

Example:

build/libs/

contains packaged application artifacts।

Because this is generated output:

build/

should stay out of Git।


.gradle/

Local Gradle caches/state may appear under:

.gradle/

This also should not normally be committed।

Distinguish it from:

gradle/wrapper/

which should be committed।


A Useful Mental Model

Repository:

build definition
+
source
+
resources
+
tests
+
documentation

Generated locally:

build output
+
IDE state
+
tool caches

The first category belongs in Git।

The second generally does not।


Capability-Oriented Structure

We previously decided our architecture is capability-oriented।

As implementation grows, source may evolve toward:

io/liveklass/ordermanagement/
├── OrderManagementApplication.java
│
├── product/
├── inventory/
├── order/
├── payment/
└── security/

Important:

Top-level packages express the business capabilities of the application.


Layer-First vs Capability-First

A layer-first structure might look like:

controller/
service/
repository/
entity/
dto/

This is common।

For a tiny project it can work।

But our backend will grow across multiple capabilities।

Suppose there are 30 controllers and services।

Then:

service/

doesn't tell you which business area you are looking at।

Capability-first structure improves locality।


Example: Layer-First

controller/
├── ProductController.java
├── InventoryController.java
└── OrderController.java

service/
├── ProductService.java
├── InventoryService.java
└── OrderService.java

repository/
├── ProductRepository.java
├── InventoryRepository.java
└── OrderRepository.java

Product-related code is spread across the entire project tree।


Example: Capability-First

product/
├── ProductController.java
├── ProductApplicationService.java
├── Product.java
└── ProductRepository.java

inventory/
├── InventoryController.java
├── InventoryApplicationService.java
├── Inventory.java
└── InventoryRepository.java

order/
├── OrderController.java
├── CreateOrder.java
├── Order.java
├── OrderItem.java
└── OrderRepository.java

Now related code is easier to discover।


But Avoid Flat Capability Packages Becoming Crowded

As order/ grows, having everything in one directory may become noisy:

order/
├── OrderController
├── OrderResponse
├── CreateOrderRequest
├── CreateOrder
├── CancelOrder
├── GetOrders
├── Order
├── OrderItem
├── OrderStatus
├── OrderRepository
├── JpaOrderRepository
...

Then responsibility-oriented subpackages may help:

order/
├── api/
├── application/
├── domain/
└── persistence/

We introduce them when needed।


Structure Should Evolve With Complexity

Initial capability:

product/

might have four classes।

No reason to create:

product/api/rest/v1/request/
product/api/rest/v1/response/
product/domain/model/entity/
product/domain/model/value/
product/infrastructure/persistence/jpa/

before complexity requires it।

That kind of structure can make a five-class feature difficult to navigate।


Too Few Folders Can Hurt

The opposite is also possible।

Putting 80 classes in:

order/

with no internal organization becomes difficult।

Therefore structure should evolve gradually։

A useful rule:

Create structure to reduce real cognitive complexity, not to predict imaginary future complexity.


What Belongs in domain/?

When we introduce:

order/domain/

it should contain Order business concepts।

Examples:

Order

OrderItem

OrderStatus

Potential value types later।

It should not contain:

OrderController

JpaOrderRepository

PaymentProviderClient

because those are not domain concepts।


What Belongs in application/?

Application-level use cases and workflow coordination।

For example:

CreateOrder

CancelOrder

GetCustomerOrders

These may depend on repositories and domain objects।

They should not parse HTTP JSON or provider-specific responses।


What Belongs in api/?

HTTP-facing components։

For example:

OrderController

CreateOrderRequest

OrderResponse

Responsibilities:

REST contract

transport validation

request/response mapping

Not:

database queries

payment-provider HTTP calls

domain state manipulation directly

What Belongs in persistence/?

Database-specific concerns।

For example:

Spring Data repositories

JPA mappings

custom database queries

persistence adapters

exact structure depending on our implementation choice।

This is where PostgreSQL/JPA details belong।


What Belongs in integration/?

External service protocol code।

For Payment:

payment/
└── integration/
    ├── PaymentGateway
    └── ProviderPaymentClient

Potentially request/response representations specific to the provider।

These should not leak into order/domain/


What Belongs in security/?

Application-wide authentication/security integration।

Examples later:

SecurityConfiguration

AuthenticatedUser

CurrentUserProvider

But customer-specific business ownership rules remain inside relevant application workflows।

Security package should not become:

all authorization logic

for the entire system।


Avoid a utils/ Dumping Ground

A common package:

utils/

often becomes a warning sign।

It may eventually contain:

OrderUtils

PriceUtils

InventoryUtils

DateUtils

ValidationUtils

The problem:

Utilities often hide responsibilities that actually belong to a domain or application component.

For example:

OrderUtils.calculateTotal(...)

probably belongs to:

Order

not generic utilities।


Not Every Utility Is Bad

Something truly technical and stateless may reasonably exist as a utility।

But ask first:

Which capability owns this behaviour?

If answer is clear, put it there।

Only genuinely generic technical functionality should become shared utility code।


Avoid common/ for Domain Concepts

Suppose Product and Order both use money values।

Someone may put:

common/Money.java

This might eventually make sense if Money truly has shared domain semantics।

But don't move a concept into common merely because two classes reference it।

Shared abstractions should have coherent meaning।


Package Visibility Can Help Boundaries

Java supports package-level visibility when no access modifier is specified।

Example:

class OrderCalculator {
}

may be package-private।

This can help keep implementation details internal।

Not every class needs to be:

public

Reducing unnecessary public surface makes architecture easier to change।


Public Classes Create Coupling

If every internal component is public, other packages can depend on it easily։

Then later refactoring becomes harder।

A useful mindset:

Make something public because another package genuinely needs it, not by default.

We will apply this selectively as code emerges।


Spring Components and Package Placement

Later Spring-managed classes may use annotations such as:

@RestController
@Service
@Repository
@Component

If they live below our root package:

io.liveklass.ordermanagement

Spring's component scanning can discover them।

That means project structure and runtime behaviour are connected।


A Component Outside the Scan Path

Suppose someone creates:

io.liveklass.payment.ProviderPaymentClient

outside:

io.liveklass.ordermanagement

Spring Boot may not discover it automatically।

This can produce runtime errors such as missing beans।

Better:

io.liveklass.ordermanagement.payment.integration

keeps component under application root and architecture boundary।


Don't Fix Bad Placement With Broad Scanning

You could write:

@ComponentScan("io.liveklass")

to scan everything।

But broad scanning can accidentally pull unrelated Spring components into the application।

Prefer deliberate package organization।


Configuration Classes

Later we may add configuration classes such as:

PaymentClientConfiguration

SecurityConfiguration

Where should they live?

Near the capability they configure when possible।

Example:

payment/integration/PaymentClientConfiguration

or:

security/SecurityConfiguration

rather than one huge:

config/

package containing every unrelated configuration class।


Is a config/ Package Always Bad?

No।

For truly application-wide configuration it may be useful।

But capability-specific configuration should usually remain close to its capability।

Otherwise config/ becomes another technical-layer dumping ground।


Resources Should Follow Ownership Too

Later Flyway migrations will live conventionally under:

src/main/resources/db/migration/

because Flyway expects database migration resources there।

This is one case where framework/tool convention appropriately shapes structure।

Not every architecture concern must be mirrored by capability directories under resources।


Source Structure vs Runtime Structure

Java package structure describes source organization।

It does not imply separate runtime processes।

For example:

product/
inventory/
order/
payment/

all compile into one application artifact।

At runtime:

one JVM
one Spring Application Context
one deployment

still holds।

This is our modular monolith।


Package Is Not a Service

This deserves emphasis:

order/

does not mean:

Order microservice

and:

inventory/

does not mean:

Inventory microservice

Packages are source-code boundaries inside one application।


Package Is Also Not Automatically an Aggregate

Domain aggregate and package are different concepts।

For example:

order/domain/

can contain:

Order
OrderItem
OrderStatus

where Order + Order Items form an aggregate।

The package itself may contain other domain concepts too।

Don't confuse:

package boundary

with:

domain consistency boundary

Build Structure Should Tell a Story

A new engineer browsing:

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

should eventually see:

product
inventory
order
payment
security

and recognize the main responsibilities of the application।

This is better than first seeing:

impl
manager
helper
common
util
misc

which say almost nothing about the business system।


Naming Matters

A package/class name should communicate purpose।

Good:

order
inventory
payment
CreateOrder
OrderRepository
PaymentGateway

Weak:

processor
handler
manager
common
helper
data

Generic names are sometimes necessary, but too many are a sign that responsibilities are unclear।


One Class Per File

Standard Java convention:

Order.java

contains the primary:

class Order

This improves discoverability and tooling।

Nested classes may have legitimate uses, but don't pack unrelated classes into a single file to reduce file count।


Should Interfaces and Implementations Be Separated by Package?

Some codebases use:

service/
service/impl/

with:

OrderService
OrderServiceImpl

This often adds little value if there is one implementation।

We are not adopting that pattern automatically।

If an interface represents a real boundary, package it according to responsibility।

Example:

payment/integration/PaymentGateway

and provider implementation nearby।

No generic /impl convention required।


Don't Mirror Framework Terminology Everywhere

Spring uses concepts such as:

Controller
Service
Repository
Component

But our packages should primarily express the application architecture։

We don't need:

controllers/
services/
repositories/
components/

at root just because Spring has those stereotypes।

Framework is supporting our system; it is not our domain model।


Test Structure Should Follow Production Structure

If main source becomes:

order/domain/Order.java

test source should commonly become:

order/domain/OrderTest.java

Application workflow:

order/application/CreateOrder.java

test:

order/application/CreateOrderTest.java

This creates predictable navigation।


Integration Tests May Need Different Naming

Later we may distinguish tests by intent:

OrderTest

for unit/domain tests։

OrderRepositoryIntegrationTest

for PostgreSQL integration tests։

OrderApiIntegrationTest

for HTTP-level integration tests।

Naming should communicate the test boundary।


Don't Create a Separate integration-test Source Set Yet

Gradle supports custom source sets such as:

src/integrationTest/java

That can be useful in mature projects।

But our current test suite is tiny।

We should not introduce complex Gradle test source-set configuration before needed।

Later test volume may justify it।


Docs Structure

Our design phase established:

docs/
├── rfcs/
├── adr/
└── incidents/

Unlike application source, these are engineering artifacts।

Once added:

docs/rfcs/001-order-management-architecture.md
docs/adr/001-use-modular-monolith.md

etc.

Documentation belongs in repository because it explains the system alongside code।


Why Not Put RFC in Confluence Only?

Companies may use external documentation platforms։

But repository-local documents have advantages:

versioned with code

easy to review in PRs

discoverable by engineers

preserve architecture history

A real company might use both।

For our project, repository docs give a coherent engineering history।


The Repository Will Grow Gradually

Current:

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

Later:

order-management/
├── build.gradle
├── settings.gradle
├── gradle/
├── src/
├── docs/
├── Dockerfile
├── compose.yaml
└── README.md

But each addition should correspond to a real project need।


Don't Create Docker Structure Yet

Production Docker setup is not part of current ticket।

Likewise:

docker/
compose.yaml

should arrive when local infrastructure/container work requires them।

The fact that final repository architecture includes them does not mean they need to exist in the first commit।


Don't Create db/migration Before Flyway Ticket

Same principle।

BACKEND-102 introduces PostgreSQL + Flyway।

That's when:

src/main/resources/db/migration/

becomes meaningful।

An empty folder contributes nothing now।


How Spring Boot Finds application.yml

Spring Boot automatically looks for conventional configuration resources such as:

application.yml

on the application classpath।

Because:

src/main/resources

is included in the packaged classpath, Spring can load the configuration during startup।

This is convention over configuration in practice।


Convention Over Configuration

Spring Boot provides defaults and standard locations so we don't manually wire every infrastructure detail।

Examples:

application.yml location

component scanning

test integration

resource handling

Using conventions reduces configuration noise।

But convention does not mean:

don't understand what Spring is doing.

Good engineers know which convention they are relying on।


Application Class Should Stay Small

Our:

OrderManagementApplication

should mostly remain bootstrap code।

Bad:

@SpringBootApplication
public class OrderManagementApplication {

    public static void main(...) {
        ...
    }

    public void createOrder() {
        ...
    }

    public void adjustInventory() {
        ...
    }
}

The application class should not become a business service।

Its responsibility is application startup/configuration root।


Where Does Business Logic Go?

Not in:

OrderManagementApplication

Not in:

application.yml

Not in:

build.gradle

Business behaviour belongs inside capability code:

order/domain

order/application

inventory/domain

depending on responsibility।

Clear structural boundaries reinforce architectural boundaries।


How Do We Decide Where a New Class Goes?

Ask:

Which capability owns this?

Then:

What responsibility does this class have?

Example:

OrderController

Capability:

Order

Responsibility:

HTTP API

Therefore:

order/api/OrderController

Another Example

CreateOrder

Capability:

Order

Responsibility:

application workflow

Therefore:

order/application/CreateOrder

Another Example

Inventory

Capability:

Inventory

Responsibility:

domain state/behaviour

Therefore:

inventory/domain/Inventory

Another Example

ProviderPaymentClient

Capability:

Payment

Responsibility:

external integration

Therefore:

payment/integration/ProviderPaymentClient

This decision method scales well।


What If You Don't Know Where a Class Belongs?

That is often useful feedback।

Suppose class:

OrderHelper

could fit nowhere clearly।

Ask what it actually does।

If it:

calculates Order total

that behaviour likely belongs in Order domain।

If it:

maps Order to HTTP response

it belongs near API।

If it:

loads Orders from PostgreSQL

it belongs near persistence।

Unclear placement may expose unclear responsibility।


Structure Can Reveal Bad Design

Imagine:

shared/BusinessLogicHelper.java

used by Product, Inventory, and Order।

That may indicate several capabilities are accidentally coupled through generic code।

Folder organization is not just cleanliness।

It can make architectural smells visible।


Circular Dependencies

Capability-oriented structure should avoid circular dependency patterns such as:

order
  ↓
inventory
  ↓
order

We previously decided application workflows should coordinate cross-capability behaviour rather than domains calling each other arbitrarily।

Project structure helps us notice such cycles।


Example: Order Cancellation

Bad:

order/domain/Order
    imports
inventory/application/InventoryService

Now domain is coupled to another application capability।

Better:

order/application/CancelOrder

coordinates:

Order

Inventory

while domain objects remain focused।


Package Boundaries Are Guidelines, Not Magic

Java packages cannot automatically enforce every architecture rule।

An engineer can still import the wrong class।

So structure must be supported by:

team conventions

code review

tests

design documentation

Architecture is a social + technical system।

Not just folders।


Avoid Deep Package Hierarchies

Too deep:

io.liveklass.ordermanagement.order.application.usecase.command.handler.impl

This is difficult to navigate and adds very little semantic value।

Prefer only levels that communicate meaningful boundaries।

For example:

io.liveklass.ordermanagement.order.application

is usually enough।


Shorter Is Not Always Better Either

Flat:

io.liveklass

with every class directly beneath it loses capability structure।

Balance matters।


Package Structure Should Reflect Stable Concepts

Technical details change more often than domain capabilities।

For example:

Order
Product
Inventory

are likely stable concepts।

But:

Jpa
Rest
WebClient

are implementation choices।

Capability-first organization therefore tends to remain understandable as infrastructure changes।


Should Version Appear in Java Package?

Some teams use:

api/v1/

for API versioning।

We have not yet chosen exact API versioning implementation।

So don't create:

order/api/v1/

before that decision is made।

API versioning comes in Module 5।


Should We Put internal in Packages?

Java does not have a first-class internal visibility concept like some languages।

Some teams use:

internal/

as convention।

We don't currently need it।

Package-private visibility and clear capability structure are sufficient for now।


Structure of a Healthy Small Backend

At early stages, healthy source tree should be boring।

For example, after Product implementation begins:

io/liveklass/ordermanagement/
├── OrderManagementApplication.java
│
└── product/
    ├── api/
    ├── application/
    ├── domain/
    └── persistence/

Then Inventory arrives:

├── product/
├── inventory/

Then Order:

├── product/
├── inventory/
├── order/

The architecture emerges alongside real code।


Don't Scaffold the Entire Course

We know Payment comes later।

That does not justify generating:

payment/
payment/application/
payment/domain/
payment/integration/
payment/persistence/

today।

One principle from our design review was specifically:

Don't create a Payment domain package before a real Payment domain model exists.

The same discipline applies to code structure।


Spring Boot's Default Project Layout Is a Starting Point

Spring Boot doesn't require our exact capability packages।

It primarily expects a normal Java project and provides conventions।

Architecture remains our responsibility।

Framework can tell us:

where resources usually go

but it cannot decide:

whether Order owns Inventory

or:

where cancellation business logic belongs

Those are engineering decisions։


Framework Structure vs Domain Structure

Spring Boot gives:

main class

configuration

component scanning

resources

test support

Our architecture gives:

Product

Inventory

Order

Payment

responsibility boundaries

Both coexist।

Do not confuse framework layout with application design।


Application Structure and Future Deployment

Because everything is in one Gradle project and Spring Boot application:

all capability code
        ↓
one build artifact
        ↓
one container
        ↓
one deployed application

This matches ADR-001।

Our source structure supports modularity without changing deployment model։


Review the Current Repository

For BACKEND-101 at this point, repository should remain small।

order-management/
├── .gitignore
├── README.md
├── 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/
                └── OrderManagementApplicationTests.java

Nothing here is unnecessary।

Nothing here pretends future features already exist।


Project Structure Review Checklist

When reviewing project structure, ask:

Is the main application class at the root package?

Are application components under that package?

Are business capabilities easy to identify?

Are generated files excluded from Git?

Is configuration separate from code?

Are tests placed predictably?

Are empty speculative packages being created?

Is `common` or `utils` becoming a dumping ground?

Are provider/database/framework details leaking into domain packages?

Is package nesting understandable?

These questions are more useful than enforcing a directory template blindly।


A Common Mistake: Organizing by File Type Forever

Technical-layer packages often begin well:

controller/
service/
repository/

But as application grows, this can hide business boundaries।

Our choice of capability-oriented structure is intentional and tied to the architecture we already reviewed।

This means folder organization is not random style preference।

It follows system responsibilities।


A Common Mistake: Overusing Service

Developers may place everything in:

service/

Then classes become:

OrderService
PaymentService
ProductService
InventoryService
NotificationService
ValidationService

The word Service tells little about whether the class:

coordinates a use case

implements domain behaviour

talks to an external API

loads data

Our structure should make responsibility more explicit।


A Common Mistake: DTO Everywhere

Developers sometimes create:

dto/

at root containing:

ProductDto
OrderDto
InventoryDto
PaymentDto

This again separates code by technical category।

Better:

order/api/OrderResponse

near the API that uses it।

Transport models belong close to their transport boundary।


A Common Mistake: Entity Package Means JPA

Some codebases use:

entity/

to mean JPA entities।

But our architecture uses the term "domain entity" differently।

To avoid confusion, package names such as:

order/domain/

are clearer for domain concepts।

Persistence representation, if separate later, can live under:

order/persistence/

A Common Mistake: Creating One Global Repository Package

Global:

repository/

makes database access easy to discover technically but weakens ownership։

For example:

InventoryRepository

should clearly belong to Inventory capability।

Capability ownership matters especially when cross-capability workflows begin।


A Common Mistake: Hiding Everything Behind Manager

Class:

OrderManager

could mean almost anything।

Does it:

create orders?

query orders?

manage state?

call payment provider?

perform admin tasks?

Prefer names based on meaningful responsibility।


Structure and Code Review

Suppose PR adds:

order/domain/PaymentProviderClient.java

Reviewer can immediately ask:

Why is external provider infrastructure inside Order domain?

A clear directory structure makes responsibility violations visually obvious।

That is valuable।


Structure and Onboarding

Imagine a new engineer joins the team six months later।

They need to change Order cancellation।

With clear structure:

order/

is the obvious starting point।

Within it:

application/CancelOrder

and:

domain/Order

provide a clear path।

Good architecture reduces time spent searching।


Structure and Refactoring

If Payment Provider implementation changes later:

payment/integration/

gives a predictable boundary।

If API changes:

order/api/

is the likely location।

If business lifecycle changes:

order/domain/

is where we expect core behaviour।

This is architecture helping maintenance।


Structure Is Not Immutable

Suppose Product capability remains tiny।

We may keep:

product/

flat for a while।

Later it grows。

Then refactor to:

product/
├── api/
├── application/
├── domain/
└── persistence/

Refactoring package organization as complexity changes is normal।

Don't treat initial directories as sacred architecture contracts।


But Avoid Constant Restructuring

The opposite problem:

reorganize packages every week

creates noise and merge conflicts।

Refactor structure when:

current layout creates real navigation or responsibility problems

not because a new blog post recommends another architecture style।


What We Should Remember About Spring Boot Structure

Spring Boot gives us several useful conventions:

main application class

component scanning from root package

src/main/resources

application.yml

src/test/java

Gradle integration

Our job is to build a business architecture cleanly inside those conventions।


BACKEND-101 Progress

After this lesson, our bootstrap ticket is stronger because we understand the project rather than merely generating it।

We now know:

why the main class lives at the root package

why package placement affects component scanning

why build and runtime config are separate

why tests mirror source structure

why capability-oriented packages will be introduced gradually

why generated artifacts don't belong in Git

Engineering Principle

The core principle:

Project structure should make system responsibilities easier to discover, not merely sort files by extension or framework stereotype.

Another:

Use Spring Boot conventions where they help, but let business capabilities shape the application architecture.

And:

Create packages and abstractions when real responsibilities exist—not as placeholders for future code.


Summary

In this lesson, we learned that:

  • Repository structure is part of maintainability and architecture.
  • settings.gradle defines the Gradle project identity.
  • build.gradle defines build-time concerns such as plugins, dependencies, Java version, and tests.
  • Runtime configuration belongs separately in resources such as application.yml.
  • Gradle Wrapper is committed because it supports reproducible builds.
  • src/main/java contains production Java source.
  • src/main/resources contains runtime resources and configuration.
  • src/test/java contains automated test code.
  • Generated build/ and local .gradle/ state do not belong in source control.
  • The main @SpringBootApplication class should remain at the root application package.
  • Spring Boot's default component scanning naturally discovers components in subpackages beneath the main class.
  • Good package placement is preferable to unnecessary custom component scanning.
  • Our backend will organize code primarily around Product, Inventory, Order, Payment, and Security capabilities.
  • Capability packages do not imply Gradle subprojects or microservices.
  • Responsibilities such as API, application, domain, persistence, and integration may become subpackages when real complexity justifies them.
  • We should avoid speculative empty package trees.
  • common, utils, service, and manager packages can become dumping grounds if responsibilities are unclear.
  • Domain code should not contain HTTP, database, or provider-specific infrastructure concerns.
  • Tests should usually mirror the concepts they protect.
  • Package organization should evolve gradually with the system rather than being over-designed upfront.
  • Structure should help a new engineer quickly understand where a change belongs.

Next lesson:

Dependency Injection

There we will introduce the first major Spring concept behind real backend composition: how components receive the dependencies they need without constructing infrastructure themselves, why direct object creation can create tight coupling, how constructor injection works, and how Dependency Injection supports the responsibility boundaries we already designed.