CodeGym /Courses /Spring Boot /Initializr: structure and starter dependencies

Initializr: structure and starter dependencies

Spring Boot
Level 3, Lesson 3
Available

1. Why it is worth dissecting the skeleton before the code

When a beginner opens a Spring Boot project for the first time, they often see not an "orderly framework", but a "forest of folders". That is a normal reaction: the brain honestly admits that it does not yet know how to distinguish what matters from what does not. In this lecture, we will make the project readable, so that you stop being afraid of the structure and start using it as a hint rather than as a test of faith.

After build.gradle.kts, the picture is still incomplete: the build rules are already clear, but it is still not obvious what they turn into on disk. So now we will literally "open the box" of the generated framework: folders, classes, resources, and starters.

The most useful idea: Initializr generates a project not because "that is the convention", but because Boot and Gradle rely on conventions. If the project follows those conventions, the tools know where to look for code, where to look for resources, where to find tests, how to run the build, and how to package the result.

It is convenient to keep a simple scheme in mind: in any normal Gradle project (and in a Boot project too), files are divided into several areas of responsibility.

flowchart TD
  A[Project catalog-service] --> B[Build infrastructure]
  A --> C[Application code]
  A --> D[Resources]
  A --> E[Tests]

  B --> B1[gradlew / gradle/]
  B --> B2[build.gradle.kts / settings.gradle.kts]
  C --> C1[src/main/java]
  D --> D1[src/main/resources]
  E --> E1[src/test/java]

Until you learn to "read" this map, the IDE looks like a closet where everything is mixed together: socks are next to pots, and a passport is between grocery bags. Once you memorise the areas, the project turns into a familiar apartment: the kitchen is in the kitchen, the bedroom is in the bedroom, and the cat... well, the cat is still somewhere on the keyboard, but at least you know the layout.

2. Project map: the repository root

The project root is the place where a beginner usually starts to panic: "Why are there so many files here, and I have not written a single line of business code yet?" Calm down: most files in the root are not application code, but an instruction on how to build and run the project. It is like the box for a device: you have not turned the unit on yet, but you already have the manual, warranty, and charger.

In a typical catalog-service project created through Initializr for Gradle, you will see something like this:

catalog-service/
├─ build.gradle.kts
├─ settings.gradle.kts
├─ gradlew
├─ gradlew.bat
├─ gradle/
│  └─ wrapper/
│     ├─ gradle-wrapper.jar
│     └─ gradle-wrapper.properties
└─ src/
   ├─ main/
   └─ test/

Let's break this down by meaning. The table below is not there so that you "memorise everything", but so that you get used to this: every file in the root has its role.

What you see What it is Why it matters
build.gradle.kts the main Gradle build script describes dependencies, plugins, and the project build
settings.gradle.kts Gradle project settings minimum: the project name (root project name)
gradlew /
gradlew.bat
Gradle Wrapper scripts run the correct version of Gradle from the project
gradle/wrapper/* Wrapper "internals" the Gradle version and the way to download it are pinned here
src/ source files this is already the "application territory": code, resources, tests

It is psychologically important to understand this: the project root is first and foremost infrastructure, not business logic. That does not mean these files are "unimportant". It means you are not required to understand them right now at the level of a Gradle plugin architect. It is enough to understand that without them the project loses reproducibility.

For orientation, here is an example of a minimal settings.gradle.kts that is usually generated:

// Root project name: this is what the IDE will show, and it is often used in the build
rootProject.name = "catalog-service"

One file, one line — and still useful: from it you get the module name in the IDE and the base name of the artifact (and it is simply pleasant to read).

3. The src folder: the main divider between "code / resources / tests"

The src folder is where a beginner often sighs with relief: "Oh, finally something that looks like code." But even here it is important not to turn the project into a dumping ground. The main point of src is that Gradle and Java projects have long agreed on a standard layout: where code lives, where resources live, where tests live. This agreement saves you a lot of manual configuration.

Inside src you will almost always see two top-level directories: main and test. This is not a "Spring whim", but a general engineering standard: the application and the tests are separated physically.

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

src/test/resources may appear later — when tests need files and data (JSON, SQL scripts, fixtures, etc.).

The idea is this: everything in src/main belongs to the "production" application, that is, what is ultimately run. Everything in src/test belongs to the test code, that is, what helps verify the application.

Another useful, down-to-earth idea: Gradle does not try to guess where your Java code is. It simply looks in the standard folders. So if you put a Java class in src/main/resources, it will not become a "resource with intelligence"; it will become a problem.

To make it easier to remember, you can keep a short table in mind:

Path What is there How to think about it
src/main/java application source code (Java) "what gets compiled"
src/main/resources resources (non-Java) "what is placed alongside the application"
src/test/java Java tests "what verifies the application"
src/test/resources resources for tests "data/files needed by tests"

The point of this separation becomes especially nice when the project grows. Right now it feels like bureaucracy. Later it feels like order on your desk: when you know where the charger is, you do not feel like an archaeologist every evening.

4. src/main/java: the base package and the main application class

src/main/java is the most "alive" part of the project: this is where your application code will live. But even here Initializr does not create chaos; it immediately sets the right starting point: it creates the base package and the main class. At this stage, it is especially important to understand that a package in Java is not just a string in package ...;, but also a folder structure and the "visibility boundaries" for component scanning.

If you chose, for example, group = com.example and artifact = catalog-service, then Initializr usually builds the base package as:

com.example + catalogservice  →  com.example.catalogservice

And then the path to the main class will be:

src/main/java/com/example/catalogservice/CatalogServiceApplication.java

Inside is a very minimal Boot entry point: a class with main() and an annotation that tells Boot: "Start here."

package com.example.catalogservice;

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

// Main application class: Spring Boot starts here and brings up the context
@SpringBootApplication
public class CatalogServiceApplication {

    public static void main(String[] args) {
        // Starting the Spring Boot application (the context, auto-configuration, embedded server, etc. are brought up)
        SpringApplication.run(CatalogServiceApplication.class, args);
    }
}

What matters now is not to "dissect the magic of @SpringBootApplication piece by piece" (that will be a separate topic in the course), but to see the purpose of the file: this is the application entry point, from which it starts. In ordinary Java terms, it is similar to the main method of a console program, only here it brings up an entire runtime and container.

Another important point from our Spring Core review: the location of this class in the package matters. Very roughly (and for now without details), you can think like this: Spring Boot looks at the package of the main class and treats it as the "root" for searching your components. That is why a good habit is to keep the main class in a neat top-level package of the project, rather than hiding it somewhere deep like com.example.catalogservice.utils.startup.launcher.finalversion2.

If you later add subpackages (for example, catalog, config, and so on), they will logically sit "below" the base package, and the project will be naturally readable.

5. src/test/java: the test scaffold that appears immediately

The src/test/java folder often surprises beginners: "I am not testing anything yet, so why did I already get a test?" In fact, this is a very good engineering hint from Initializr: a good service is not only code, but also a minimal check that it is capable of starting at all. Even if you are not ready to think about a test strategy yet, it is useful to know that the scaffold is already there.

Initializr usually creates a test in the same package as the main class:

src/test/java/com/example/catalogservice/CatalogServiceApplicationTests.java

Here is the typical "empty-looking" test that gets generated for you:

package com.example.catalogservice;

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

// Integration test: brings up the Spring context and checks that the application starts at all
@SpringBootTest
class CatalogServiceApplicationTests {

    @Test
    void contextLoads() {
        // The body may be empty: if the context does not start, the test will fail before this method is executed
        // In this course, we assume this runs on JUnit 6 (the @Test annotation has the same meaning)
    }
}

From a beginner's point of view, this looks almost like a joke: the method is empty, but the file exists. But the point is exactly that if the application context could not start, the test will fail before it even begins executing your code inside contextLoads(). So this test answers the question: "Is the project alive at all? Does it build? Does it start at least at the context level?"

We are not going into the details of @SpringBootTest, JUnit 6, and testing approaches today — we are simply recording this: tests appear not because "the handout says so", but because the project skeleton must have a minimal safety rope. Even if you are not a climber yet, the rope is already attached — and that is good.

6. src/main/resources: resources and discipline

src/main/resources is where everything the application needs but that is not Java code lives. Beginners usually go to one of two extremes here: some ignore this folder ("there is some kind of magic there"), while others start putting everything into it ("well, these are just files"). The truth is in the middle: resources are an important layer of the application, but they have their own discipline.

A typical generated project contains an application configuration file. In the generated project, you may see application.properties here — this is the ordinary default option in Initializr, not a second equal line of the project. In catalog-service, we follow a YAML-first approach, so we consider application.yaml the working format, not a mix of two formats.

src/main/resources/
└─ application.yaml

A minimal content example may look like this:

spring:
  application:
    name: catalog-service

If after generation you have application.properties, that is not a problem: what matters is the resources folder itself and the place where the settings live.

The idea of the resources folder is that its contents end up on the application classpath. Put simply, these are files that the application can "see" at runtime. Later this may include static files, configs, templates, localisations, and so on — but today it is important simply to learn how to distinguish code from resources and not to mix the folders up.

The most common beginner mistake here looks like this: they create a Java class in resources because "it is more convenient in the IDE". Then Gradle does not compile it, Spring does not see it, and the student thinks Spring is broken. In reality, Spring is not broken — it is simply not required to look for Java code where resources are supposed to live by convention.

7. Starter dependencies in the skeleton

After the folder map, a natural question arises: why does this scaffold already look alive even though there are so few lines in dependencies? The answer is exactly in starters.

When you first look at build.gradle.kts, you may feel something strange: "I have only two dependency lines, but the project looks like a full server. What is the catch?" There is no catch: this is the philosophy of Spring Boot. Instead of forcing a beginner to assemble infrastructure piece by piece, Boot offers an entry through starters — ready-made dependency sets for a typical scenario.

In the Initializr form, such a dependency may be hidden under a level name like Spring Web, but in build.gradle.kts the canonical course line remains org.springframework.boot:spring-boot-starter-webmvc.

dependencies {
    // Base web starter for the course: brings in Spring MVC, the embedded server, and everything typical for an HTTP application
    implementation("org.springframework.boot:spring-boot-starter-webmvc")

    // Test starter: Spring test infrastructure + JUnit 6
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

What matters now is only the meaning, without going into the details of the internal composition. spring-boot-starter-webmvc is a "ticket" into the web world (server-side, MVC infrastructure, and everything needed for the application to work as an HTTP service at all). spring-boot-starter-test is a similar "ticket" into the testing world (JUnit 6 and useful test infrastructure).

Notice an important psychological point: you connected a starter, not "one library". So there are few lines, but the effect is large. This is not magic, but a preselected set of dependencies. And that is one of the reasons Boot projects start so pleasantly: you describe the scenario ("I need web"), and the platform pulls in the base set of pieces.

You may also notice that dependencies usually do not have a version specified. That is normal for the Boot approach: Initializr and the Boot plugin are configured so that the project stays on a single aligned library line. Today it is enough to record: "a version is not always needed in the dependency line", and not try to fix this by adding random :1.2.3 "just in case".

8. Mini check: what is already ready

At this point, it is useful to breathe out a little and admit: you already have a project, even though you have written almost no code. But the project is intentionally empty in terms of functionality for now. It is like a new notebook: there are pages, there is a pen, there is a table of contents, but the novel has not been written yet. And that is good — we want to start with a clear scaffold, not with chaos.

If we describe the state of catalog-service after Initializr in one paragraph, it would be this: the root contains build infrastructure (Gradle and Wrapper), src/main/java contains the application entry point, src/test/java contains a minimal "context loads" test, and src/main/resources has space for resources and configuration. That is enough for the project to sync with the IDE, build, and be able to run through standard Gradle commands.

At this stage, the best strategy is not to "improve" the structure manually. Do not move the Wrapper files "because they are in the way". Do not rename packages in ten places "because you feel like it". Do not create a my_code folder next to src "because it is easier". The scaffold is valuable because it is standard: standardisation is your future time savings.

9. Typical mistakes when dissecting a project skeleton

Mistake #1: treating the generated project as a "pile of junk that can be safely cleaned up".
Often the hand reaches for gradlew, the gradle/ folder, and half the files in the root because "they do not look like code". This breaks build reproducibility and turns the project into something that "works only on my laptop". If you are not sure why a file is needed, it is better to understand its role first rather than delete it.

Mistake #2: confusing src/main/java and src/main/resources.
This mistake looks funny right up until the moment you spend an hour asking "why does Spring not see my class" when it is sitting in resources. Java code should live in src/main/java, resources in src/main/resources. This is not a whim; it is the convention on which Gradle and the typical Java ecosystem stand.

Mistake #3: chaotically changing the base package and the main class name.
The package of the main class is not only about "nice naming", but also an important structural point of the project. If you rename com.example.catalogservice to com.qwerty.myapp2.final, you will later be surprised that everything around it also has to be renamed: folders, imports, tests. In an educational project, it is better to keep stable, readable names and not do a rebrand every half hour.

Mistake #4: ignoring the test scaffold because "I am not doing tests".
The contextLoads() test looks empty, and you may want to delete it as useless. But it serves as the first signal: the project starts as an application, the context is assembled. Even if you are not writing tests yet, having a minimal check is like having a first-aid kit in the car: it is better if it is there and never needed.

Mistake #5: thinking that a starter is "one library", and trying to replace it with a manual set of modules.
Sometimes beginners see "starter" and decide: "Oh, I am clever, I will replace the starter with three or four modules so everything is under control." That usually ends with something not being pulled in, versions drifting apart, and the project no longer starting. A starter is a convenient entry point. In this course, we stick to the starter approach so that the project remains simple and predictable.

1
Task
Spring Boot, level 3, lesson 3
Locked
Files in the right places in the generated skeleton
Files in the right places in the generated skeleton
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION