Study Notes
Spring Boot by Buildingfoundation

The Layer Map

The map for the whole workshop: controllers, services, repositories, models, JPA, Lombok, Mockito, dependency injection, and where each concern belongs.

25 min read·verified Spring Boot 4.x·reviewed 2026-08-28

After this you should be able to

  • Name the main application layers
  • Place a concern in its layer
LayersDIMental Model

Spring Boot by Building · Chapter 1

Before a single line of code: the one picture that untangles controllers, services, repositories, models — and where JPA, Lombok, Mockito, @Autowired, and "which database" each hang on that picture. Come back to this page whenever a Spring project feels like a pile of unrelated files.

Chapter 1 · Orientation · Read ~13 min · No code to run yet


Choose your route#

Study the same The Layer Map explanation with a different purpose: build and verify it in Learn deeply, or retrieve and defend it in Prepare for interviews.

Learning goal
  • Name the main application layers
  • Place a concern in its layer

Shared core#

The chapter below remains the canonical explanation for both goals. Keep its model, examples, and trade-offs shared; only the deliberate practice changes with the route.

Learn deeply: build evidence#

Use this chapter’s TaskFlow work to make one small, observable change. Run the stated acceptance checks before recording practice.

Prepare for interviews: explain the trade-off#

Without notes, explain the boundary this chapter protects, one concrete implementation decision, and the cost of a tempting shortcut. Then answer one follow-up using the chapter’s own example.

Learn next: Use name the main application layers as the base, then run a spring boot app in From an empty folder to a running endpoint. Interview next: Use name the main application layers as the base, then wire controller service and repository in Controller -> Service -> Repository, wired.

Continue to From an empty folder to a running endpoint

// the big idea

A Spring Boot backend is not a pile of files. It is one short assembly line that a web request walks down and back. There are only about four kinds of class, each with exactly one job, always in the same order. The annotations (@RestController, @Service, …) are just name tags that tell Spring which kind a class is. Learn the line once and every unfamiliar Spring project becomes readable — because it is always the same line.

// why you feel tangled (and why it's not your fault)

Most tutorials introduce the pieces alphabetically or by feature — a bit of JPA here, an @Autowired there, Mockito in a testing video — so they arrive as a bag of unrelated words. They are not unrelated. Every one of them lives at a specific seam of the same assembly line. This chapter puts each word back where it belongs on one map. Once each term has a home, "getting messed up" mostly stops.

Section 1The line a request walks#

Every request to a Spring backend does the same trip: it enters at the top, is handed down one layer at a time, reaches the database, and the answer is handed back up the same steps in reverse. Read this diagram top-to-bottom, then bottom-to-top.

Client browser · Postman · curl · another app @RestController Controller talks HTTP: reads the URL & body, returns JSON @Service Service the rules: decisions, validation, "what should happen" @Repository / JpaRepository Repository the only layer that talks to the database Database H2 · MySQL · PostgreSQL · MongoDB request down ↓ response up ↑ Model the Task object itself rides through every layer @Entity marks it a DB row

Fig 1 — The assembly line. Grey arrows: the request going down. Ochre arrows: the answer coming back up. The Model is the parcel that rides the belt.

// analogy — a restaurant

The Controller is the waiter: takes your order, brings your plate, never cooks. The Service is the chef: decides how the dish is made, the actual rules. The Repository is the pantry clerk: the only one allowed into the storeroom (the database). The Model is the dish that moves between them. You would never ask the waiter to unlock the pantry — and you never let a Controller touch the database. That single rule explains the whole structure.


Section 2The four kinds of class#

That is the entire cast. Memorise this table; it is the Rosetta Stone for reading any Spring file. When you open an unfamiliar class, your first question is always "which row is this?"

LayerOne jobName tagIs allowed to talk to…Restaurant
ControllerTranslate HTTP ⇄ Java. Read the URL/body, call a service, return the result.@RestControllerthe Service (below it)Waiter
ServiceThe business rules. Decide, validate, combine. No HTTP words, no SQL words.@Serviceone or more RepositoriesChef
RepositoryLoad and save data. The only layer that knows the database exists.@Repository / extends JpaRepositorythe DatabasePantry clerk
ModelHold the data. A plain object — a Task, a User. Rides through every layer.@Entity (when it maps to a table)nobody — it's just dataThe dish
// the one rule that keeps it untangled

Each layer may only call the layer directly below it. Controller → Service → Repository → DB. A Controller must never call a Repository directly, and nothing skips the Service. When you feel tempted to shortcut, that temptation is exactly the "tangle" — resisting it is what keeps the four kinds separate and testable.


Section 3How the pieces get connected — IoC & DI, concretely#

You now have four kinds of class. But who creates them, and how does the Controller get hold of the Service? This is the part that mystifies everyone, and it is genuinely two ideas wearing scary names.

Inversion of Control (IoC)

You stop calling new. Spring creates your objects for you and holds them in a box called the application context. One shared instance of each is called a bean.

Dependency Injection (DI)

Spring hands each object the others it needs. Your Controller declares "I need a TaskService" and Spring passes one in. You never build the chain yourself.

The two name tags do the two jobs. @Service (and its siblings) says "Spring, please build one of these and keep it in the box." A constructor says "and to build me, you'll need to hand me these."

TaskController.java — the whole wiring in six lines
@RestController               // tag: "I'm a controller — build me, and route HTTP to me"
public class TaskController {

    private final TaskService service;   // I depend on the chef

    // Spring sees this constructor and injects a TaskService bean automatically.
    public TaskController(TaskService service) {
        this.service = service;          // DI just happened — no `new` anywhere
    }
}
read it like this
  • @RestController → Spring makes exactly one TaskController at startup and keeps it.
  • The constructor asks for a TaskService. Spring already made one (because of its @Service tag), so it passes that same instance in.
  • You wrote no new TaskController(...) and no new TaskService(...). That absence is Inversion of Control.
// about @Autowired

You will see @Autowired on fields in older code — it is the older way to ask for a dependency. Modern Spring injects through the constructor automatically (as above), and that is what this course uses: it needs no annotation, it makes the field final, and — crucially — it lets you create the class yourself in a test by just passing a fake in. Same idea, cleaner seam. When you meet @Autowired, read it as "inject here"; when you write new code, prefer the constructor.


Section 4Where every buzzword lives on the map#

This is the de-tangler. Every intimidating word you listed is pinned to one seam of the same line. Read the "sits at" column and the fog lifts.

WordSits atWhat it actually does
Spring Bootthe whole appRuns the assembly line: creates the beans, wires DI, starts a web server inside your app. The factory floor.
@RestController / @GetMappingController ↔ HTTPMap a URL + HTTP verb to a Java method; turn the return value into JSON.
@Service, @Autowired, constructorController ↔ Service seamDeclare a bean and inject it. The connective tissue between layers.
Repository / JpaRepositoryService ↔ DB seamAn interface of load/save methods. You declare it; Spring writes the implementation.
JPA / Spring Data JPARepository ↔ relational DBThe rulebook for turning Java objects into table rows and back, so you rarely write SQL by hand.
@Entity, @Id, @Columnthe ModelTags on a model class that say "this class is a table, this field is the primary key," etc.
Lombok (@Getter, @Data, @Builder)the Model (mostly)A helper that generates getters/setters/constructors at compile time so your model isn't 200 lines of boilerplate. Pure convenience — removes nothing conceptual.
DTOController edgeA small "shape" object for the request/response body, so the outside world never sees your raw entity. Optional early on; matters as apps grow.
application.propertiesoutside the layersThe settings sheet: which database, what port, log levels. Change the DB here without touching a single class.
Mockito / @Mocktests onlyIn a unit test, replaces a real neighbour (e.g. the Repository) with a fake you control, so you can test the Service alone. Never ships in your running app.
H2 / MySQL / MongoDBthe Database boxInterchangeable back-ends behind the Repository seam. Same seam, different driver + config.
// the mental move that fixes "it all gets messed up"

When a new term shows up, don't ask "what is this?" first. Ask "which seam does it sit at?" Testing word → tests column. Table word → the Model. Load/save word → the Repository seam. Injection word → between two layers. Almost every Spring term answers to one seam, and once it has a seat it stops floating around confusing everything else.


Section 5"MySQL or Mongo or Java's own DB?" — untangled#

This one deserves its own section because it trips everyone. The key insight: the database is behind a seam. Your Service asks the Repository to "save this task" and does not care what is on the other side. Swapping databases is mostly a change to application.properties, not to your classes.

OptionWhat it isSetup costUse it when
H2A tiny database written in Java that runs inside your app, usually in memory. "Java's internal DB."Zero — no install, add one dependency.Learning & tests. We use this for every walkthrough so nothing external can break.
MySQL / PostgreSQLReal relational databases (tables, rows, SQL) running as a separate program.Install/run the server + set a URL & password.Real apps with structured, related data. Same JPA code as H2.
MongoDBA document database — stores JSON-like documents, not tables. Different query model.Install/run + a different Spring Data dependency.Loose or nested data. Uses MongoRepository instead of JpaRepository.
// the reassuring part

H2 and MySQL are the same JPA code — you literally change three lines of config to go from one to the other (Chapter 4 shows the exact diff). Mongo is the one real fork: tables become documents and JpaRepository becomes MongoRepository. So there are really only two persistence worlds to hold in your head — "relational (JPA)" and "document (Mongo)" — not five. Start relational; it's the bigger, more transferable half.


Section 6Where the files physically live#

Layers are also folders (Java calls them packages). The convention is one package per kind. When you open a real project, this is what you'll see — and now you can predict every folder's job:

com.example.taskflow ├─ TaskflowApplication.java // the ignition — has main(), boots the whole line ├─ controller/ │ └─ TaskController.java // @RestController — the waiter ├─ service/ │ └─ TaskService.java // @Service — the chef (the rules) ├─ repository/ │ └─ TaskRepository.java // extends JpaRepository — the pantry clerk ├─ model/ │ └─ Task.java // @Entity — the dish (one row = one Task) └─ dto/ // (optional) request/response shapes └─ TaskRequest.java src/main/resources └─ application.properties // which DB, what port — the settings sheet src/test/java/com/example/taskflow └─ service/ └─ TaskServiceTest.java // Mockito lives here, never in main/
// notice

The folder tree is Fig 1 turned sideways. controller/, service/, repository/, model/ — the same four kinds, now as directories. Tests sit in a parallel src/test world so test-only tools like Mockito can never leak into what ships. If you ever feel lost in a repo, find these four folders first; they orient you instantly.


Section 7A method: read any Spring class in ten seconds#

You said you can read Spring code now but not crystal clear. Here is a repeatable routine to make it crisp. Run these four questions, in order, on any class you open:

  1. Which kind is it? Look at the class annotation. @RestController, @Service, @Repository/JpaRepository, or @Entity → you now know its one job and where it sits on the line.
  2. What does it depend on? Look at the constructor (or @Autowired fields). Those are the layers below it that Spring will inject. That's the class's outgoing arrows.
  3. What does it expose? For a Controller, the @GetMapping/@PostMapping methods are its public URLs. For a Service, its public methods are the rules it offers upward.
  4. Where does the data stop? Follow the calls down until you hit the Repository — that's the floor, where it meets the database. Everything above is just handing the parcel along.
// try it now, then again after each chapter

You don't have code to try this on yet — but you will after Chapter 2. Keep this routine open in a tab; run it on the very first file you build. The confusion you described is mostly the absence of a fixed reading order. This is that order.


Notebook Summary#

Hand-copyable revision sheet · the whole chapter on one page

The line#

  • Client → Controller → Service → Repository → DB, and the answer back up.
  • Each layer calls only the one directly below. No skipping. No Controller touching the DB.
  • Aid: waiter → chef → pantry clerk → storeroom.

The four kinds#

  • Controller (@RestController) → HTTP ⇄ Java
  • Service (@Service) → the rules
  • Repository (JpaRepository) → load/save, the only DB-talker
  • Model (@Entity) → the data that rides the belt

Wiring#

  • IoC → you stop calling new; Spring makes the objects (beans).
  • DI → Spring hands each object what it needs, via the constructor.
  • @Autowired → the older field-injection way; prefer the constructor.

Where the words live#

  • JPA / @Entity / Lombok → around the Model + Repository seam
  • Mockito / @Mock → tests only, never in main/
  • application.properties → which DB + port, outside the layers
  • H2 = in-app DB (learning); MySQL = same JPA code; Mongo = the one real fork (documents)

Box these#

  • ⭐Ask "which seam?" before "what is it?". Every term has a home on the line.
  • ⭐Read any class with the 4 questions: kind → depends on → exposes → where data stops.
  • ⚠Controller calling a Repository directly = the tangle. Always go through the Service.
  • 💡Swapping H2 → MySQL is config, not code. Relational vs document is the only real fork.
// next

Chapter 2 — From an empty folder to a running endpoint. You'll generate a real project, meet every generated file, and get one URL responding in the browser. Fully guided, end to end.

TaskFlow checkpoint#

Current state: no project yet. Outcome: a layer map and package plan for the TaskFlow API. Prerequisite: you can read a Java interface; continue if you cannot name every Spring annotation yet. Java bridge: interfaces make collaborator boundaries explicit. Estimate: 20 minutes.

Mission: label the first slice before coding#

Sketch controller, service, repository, and model. Trace GET /tasks down and its JSON response back up. The artifact is a package plan, not a running app.

  • You can point to the one layer that owns HTTP, rules, and persistence.
  • You can explain why a controller does not call a repository.
  • Your map names the Task model travelling through the flow.

Likely files next: controller/PingController.java, TaskflowApplication.java. Suggested commit: docs: map taskflow layers.

Hint 1
Start at the browser, not the database.
Hint 2
Put decisions in the service, even if it has only one method.
Hint 3
A repository has no HTTP vocabulary.

Before opening the solution: draw the request arrow, name every layer once, and say where a validation rule would live.

Reference patch — package plan
controller → service → repository is the dependency direction; model is shared data, not a fourth caller.

Recall before continuing#

  1. Which layer translates HTTP?
  2. Where do business rules belong?
  3. Who is allowed to talk to the database?

The next workshop turns this plan into one observable URL, so the map gains a real request to trace.