Mosh Spring Boot Architecture Skill

Apply Mosh Hamedani's structured Spring Boot methodology to design, build, and debug a well-architected Java web application with proper dependency injection, bean management, and project configuration.

// TL;DR

The Mosh Spring Boot Architecture Skill applies Mosh Hamedani's structured methodology for designing, building, and debugging well-architected Java web applications with Spring Boot. It covers dependency injection via constructor injection, IoC bean management, starter dependencies, parent POM version inheritance, Maven Wrapper reproducibility, and Spring MVC. Use it when starting a new Spring Boot project, restructuring an existing Java application, or applying correct DI patterns, bean configuration, and project layout. It flags prerequisite gaps (Java OOP, SQL) before you write code and enforces best practices like programming against interfaces and the Open Closed Principle.

// When should you use the Mosh Spring Boot Architecture Skill?

Use this skill whenever you are starting a new Spring Boot project, structuring an existing Java application, or need to apply correct dependency injection patterns, bean configuration, or project layout decisions.

// What do you need before applying this Spring Boot methodology?

  • Application domainrequired
    What the application does (e.g., e-commerce store, booking system)
  • Key dependenciesrequired
    External services or libraries the app needs (e.g., payment provider, database type)
  • Java and SQL proficiency level
    Whether the user is comfortable with OOP, interfaces, and basic SQL — determines which gaps to flag
  • Build tool preference
    Maven or Gradle — defaults to Maven per Mosh's recommendation
  • IDE
    IntelliJ IDEA (Ultimate recommended), VS Code, or Eclipse

// What core principles guide Mosh's Spring Boot architecture?

Starter Dependency Principle

Never add individual libraries when a Spring Boot starter exists. Starters are curated, tested collections of compatible libraries. Always remove the version tag from starter dependencies and let the parent POM (spring-boot-starter-parent) manage versioning — this prevents version conflicts and simplifies upgrades.

Programming Against Interfaces

Classes should depend on interfaces, not concrete implementations. This decouples components so that swapping one implementation for another (e.g., Stripe for PayPal) requires zero changes to the dependent class.

Open Closed Principle

A class should be open for extension but closed for modification. Add new functionality by creating new classes rather than changing existing ones. Apply this with common sense — it is a tool, not a rule to be applied blindly everywhere.

Constructor Injection as Default

Inject required dependencies via Constructor, not Setter. Setter injection is only appropriate for optional dependencies. Constructor injection makes missing dependencies fail loudly at startup rather than silently at runtime with a NullPointerException.

IoC Container / Bean Management

Spring's IoC (Inversion of Control) container manages object creation, dependency wiring, and lifecycle. Hand control of object creation to Spring rather than managing it manually. Objects managed by Spring are called beans.

Version Inheritance via Parent POM

Spring Boot's parent POM (spring-boot-starter-parent → spring-boot-dependencies) declares tested, compatible versions for all ecosystem dependencies. Omitting explicit version tags in your pom.xml lets this parent cascade correct versions automatically.

Maven Wrapper (mvnw) for Reproducible Builds

Use the Maven Wrapper (mvnw / mvnw.cmd) rather than a globally installed Maven. It pins the exact Maven version in the wrapper config, ensuring identical builds across machines and preventing environment-specific surprises.

// How do you build a Spring Boot application step by step?

  1. 1

    Verify prerequisites before writing a single line of code

    Confirm the user has: (1) solid Java OOP knowledge — classes, methods, interfaces; (2) familiarity with relational databases and basic SQL. If either is missing, flag this explicitly and recommend closing the gap first. Do not proceed as if these are optional.

  2. 2

    Set up the development environment

    Install the latest stable JDK (verify with `java -version`). Use IntelliJ IDEA Ultimate — it has Spring Boot project generation, Maven built-in, and smart code completion built in. Community Edition is explicitly insufficient for professional Spring Boot work. On Mac, install Maven via Homebrew if not using IntelliJ. On Windows, use Chocolatey (`choco install maven`).

  3. 3

    Generate the Spring Boot project

    Use IntelliJ's built-in Spring Boot generator (only available in Ultimate) rather than start.spring.io — it avoids switching context. Set: Language=Java, Build Tool=Maven, Spring Boot=latest stable (not snapshot), Java=latest stable JDK version, Group=reverse domain (e.g., com.codewithmosh), Artifact=project name. Enable Git repository creation at this step. Leave dependency selection for Step 4.

  4. 4

    Understand and validate the project structure before adding any code

    Confirm these exist: `.idea/` (IntelliJ config — never touch), `.mvn/` (Maven Wrapper with version pinned in wrapper config), `mvnw` / `mvnw.cmd` (shell scripts for wrapper), `pom.xml` (the heart of the Maven project — group ID, artifact ID, dependencies, parent POM), `src/main/java` (Java source), `src/main/resources/application.properties` (config key-value pairs), `src/test/java` (automated tests). The entry point is the class with `SpringApplication.run()`.

  5. 5

    Add dependencies using the Starter Dependency Principle

    Use IntelliJ's dependency search (Cmd+N / Ctrl+N → Dependency) rather than manually editing pom.xml. After adding any dependency, immediately click the Maven sync icon to download it. CRITICAL: delete the `<version>` tag from any Spring Boot starter dependency — let spring-boot-starter-parent manage versions. For dev-only dependencies (e.g., Spring Boot DevTools), add `<optional>true</optional>` so they are not packaged into the production artifact.

  6. 6

    Design class dependencies using the Interface Decoupling pattern

    Identify every place one class depends on another concrete class. Extract an interface using IntelliJ's Refactor → Extract → Interface. The dependent class (e.g., OrderService) should reference the interface (e.g., PaymentService), not the concrete implementation (e.g., StripePaymentService). This enables swapping implementations without modifying the dependent class — applying the Open Closed Principle.

  7. 7

    Register beans using the correct stereotype annotation

    Annotate classes so Spring's IoC container manages them: `@Service` for business logic classes, `@Repository` for database-access classes, `@Controller` for web request handlers, `@Component` for general-purpose utilities. `@Service`, `@Repository`, and `@Controller` are all specialised aliases for `@Component`. Only annotate concrete classes — not interfaces. If two implementations of the same interface are both annotated, Spring will throw an ambiguity error (covered in disambiguation step).

  8. 8

    Wire dependencies using Constructor Injection

    Define a single Constructor in the dependent class that accepts the interface type as a parameter. Store it in a private field. If the class has exactly one Constructor, the `@Autowired` annotation is not required (Spring infers it). Only add `@Autowired` when multiple Constructors exist and you need to designate which one Spring should use. Never use field injection — it hides dependencies and breaks testability.

  9. 9

    Configure application properties in application.properties

    Use `application.properties` for all environment-specific settings: `server.port`, database credentials, custom keys like `app.page-size`. Inject property values into classes using the `@Value("${property.key}")` annotation on a private field. Never hardcode environment-specific values directly in source code.

  10. 10

    Build the web layer using Spring MVC

    Spring MVC = Model (data/business logic) + View (HTML/JSON returned to browser) + Controller (traffic director). Annotate a class with `@Controller` to make it handle HTTP requests. Use `@RequestMapping("/path")` on a method to bind it to a URL. The method returns the name of the view (e.g., `"index.html"`). Static files (HTML, CSS, JS) live in `src/main/resources/static/`.

  11. 11

    Run and debug the application

    Run via IntelliJ (Ctrl+R) or Maven Wrapper (`./mvnw spring-boot:run`). Default port is 8080 — visit `http://localhost:8080`. For debugging: (1) Print statements (`System.out.println`) for quick checks; (2) Breakpoints + IntelliJ debugger (Ctrl+D to start in debug mode, F8 to Step Over, F7 to Step Into, Option+Cmd+R to Resume). Remove all breakpoints when done — stale breakpoints interfere with future sessions. For auto-restart during development, add Spring Boot DevTools dependency and enable 'Build project automatically' + 'Allow automake' in IntelliJ settings.

// What do real Spring Boot architecture decisions look like in practice?

An application that sends notifications has a NotificationService that directly instantiates EmailNotificationService inside its method body.

Extract a `NotificationService` interface with a `send(String message)` method. Make `EmailNotificationService` implement it. Annotate `EmailNotificationService` with `@Service`. In `OrderNotificationService` (the dependent class), declare a Constructor that accepts `NotificationService` — Spring will inject the correct bean automatically. To add SMS notifications later, create `SmsNotificationService implements NotificationService` and annotate it — `OrderNotificationService` requires zero changes (Open Closed Principle in action).

A developer copies a Spring Boot dependency from Maven Central into pom.xml including the `<version>` tag, then later upgrades Spring Boot and gets dependency conflicts.

Delete the `<version>` tag from the dependency block. Because the project inherits from `spring-boot-starter-parent`, which itself inherits from `spring-boot-dependencies`, the correct compatible version is resolved automatically from the parent POM hierarchy. Upgrading Spring Boot's version in the parent tag now cascades correct versions to all managed dependencies simultaneously.

A team member on a different machine gets a different Maven version during a CI build, causing inconsistent behaviour.

Use the Maven Wrapper committed in the repository (`mvnw` on Mac/Linux, `mvnw.cmd` on Windows). The `.mvn/wrapper/maven-wrapper.properties` file pins the exact Maven version. Every machine — local and CI — will download and use that pinned version, eliminating environment-specific build surprises.

// What mistakes should you avoid when building a Spring Boot app?

  • Using IntelliJ Community Edition — it lacks the built-in Spring Boot project generator and many tools needed for productive development; always use Ultimate Edition.
  • Annotating an interface with @Service or @Component instead of the concrete implementation — Spring cannot instantiate interfaces, so no bean will be created.
  • Keeping the <version> tag on Spring Boot starter dependencies — this bypasses the parent POM's tested version management and risks incompatible library combinations.
  • Using Setter injection for required dependencies — if the setter is never called, the field is null and the application crashes with a NullPointerException at runtime instead of failing clearly at startup.
  • Adding @Autowired when only one Constructor exists — it is redundant and adds noise; @Autowired is only needed when multiple Constructors are present.
  • Annotating two concrete implementations of the same interface with @Service without resolving the ambiguity — Spring will throw a NoUniqueBeanDefinitionException.
  • Selecting a snapshot version of Spring Boot when creating a project — snapshot versions contain experimental features that may be removed; always select the latest stable release.
  • Leaving breakpoints in the code after a debugging session — stale breakpoints interrupt future debugging sessions unexpectedly.
  • Blindly applying the Open Closed Principle everywhere — it is a guideline, not a rule; over-engineering with interfaces where no variation is needed creates unnecessary complexity ('over-engineered mess').
  • Skipping lessons or jumping to a later section tag in the repository — the course and by extension this methodology builds concepts progressively; gaps cause confusion downstream.

// What are the key Spring Boot terms you need to know?

Bean
A regular Java object that is created, wired, and managed by Spring's IoC container. You declare what should be a bean via stereotype annotations; Spring handles instantiation and lifecycle.
IoC Container
Inversion of Control Container — Spring's core mechanism that inverts the responsibility of creating objects and injecting dependencies away from application code and into the framework. Accessed as `ApplicationContext` in code.
Dependency Injection
The act of passing (injecting) a dependency object into a class from outside, rather than the class creating it internally. Analogous to injecting a drug into a body — the object is supplied externally.
Starter Dependency
A curated collection of libraries and frameworks commonly used together, tested and verified by the Spring team. Adding one starter (e.g., spring-boot-starter-web) brings in all compatible libraries (Tomcat, Spring MVC, Jackson JSON, etc.) automatically.
Constructor Injection
The recommended form of dependency injection where dependencies are declared as Constructor parameters. Makes dependencies explicit and required, failing at startup if unavailable.
Setter Injection
An alternative form of dependency injection using a setter method. Only appropriate for optional dependencies — if the setter is not called, the dependency remains null.
Open Closed Principle
A class should be open for extension but closed for modification. New functionality is added by creating new classes rather than altering existing ones, reducing the risk of introducing bugs in tested code. A tool to apply with common sense, not a universal rule.
Programming Against Interfaces
Designing classes to depend on interface types rather than concrete implementations. Enables swapping implementations without modifying dependent classes.
Maven Wrapper (mvnw)
Shell scripts (`mvnw` / `mvnw.cmd`) committed to the project that download and run a specific, pinned version of Maven. Ensures reproducible builds across all environments without requiring Maven to be globally installed.
pom.xml (Project Object Model)
The heart of a Maven project. Declares project metadata, dependencies, the parent POM, and build configuration. Spring Boot dependency versions are inherited from the parent POM hierarchy.
application.properties
The configuration file in `src/main/resources` where key-value pairs define environment settings (server port, database credentials, custom app settings). Values are injected into classes using the @Value annotation.
@Component / @Service / @Repository / @Controller
Stereotype annotations that register a class as a Spring-managed bean. @Service, @Repository, and @Controller are specialised aliases for @Component, signalling intent (business logic, data access, web handling respectively).
Spring MVC (Model-View-Controller)
Spring's web framework. Model = data/business logic; View = what the user sees (HTML, JSON); Controller = handles incoming requests, coordinates model and view. Controllers are annotated with @Controller and methods mapped to URLs via @RequestMapping.
spring-boot-starter-parent
The parent POM that Spring Boot projects inherit from. It transitively inherits from spring-boot-dependencies, which declares tested, compatible versions for all Spring ecosystem libraries, eliminating the need for explicit version tags on managed dependencies.
@Value
Annotation placed on a private field to inject a value from application.properties at runtime. Syntax: @Value("${property.key}").

// FREQUENTLY ASKED QUESTIONS

What is the Mosh Spring Boot Architecture Skill?

It's a structured methodology based on Mosh Hamedani's Spring Boot course for designing, building, and debugging well-architected Java web applications. It enforces best practices like constructor injection, programming against interfaces, IoC bean management, starter dependencies, parent POM version inheritance, and Spring MVC layering — covering everything from environment setup to running and debugging your app.

What is dependency injection in Spring Boot?

Dependency injection is passing a dependency object into a class from outside rather than having the class create it internally. In Spring Boot, the IoC container handles this automatically — you declare a dependency as a constructor parameter and Spring wires the correct bean at startup, decoupling your classes and making them testable and swappable.

How do I set up a Spring Boot project the right way?

Verify you know Java OOP and basic SQL first, then install the latest stable JDK and IntelliJ IDEA Ultimate. Use IntelliJ's built-in Spring Boot generator: set Language=Java, Build Tool=Maven, latest stable Spring Boot (never snapshot), reverse-domain Group ID, and enable Git. Then validate the generated structure — pom.xml, .mvn, mvnw, src/main/java — before adding any code.

How do I wire dependencies using constructor injection in Spring?

Define a single constructor in the dependent class that accepts the interface type as a parameter and store it in a private field. If the class has exactly one constructor, Spring infers the injection — no @Autowired needed. Only add @Autowired when multiple constructors exist. Never use field or setter injection for required dependencies.

How does constructor injection compare to setter or field injection?

Constructor injection is the default because it makes dependencies explicit and required, failing loudly at startup if a bean is missing. Setter injection is only appropriate for optional dependencies — a missing setter call leaves the field null and crashes at runtime with a NullPointerException. Field injection hides dependencies and breaks testability, so avoid it entirely.

When should I use starter dependencies instead of individual libraries?

Always use a Spring Boot starter when one exists. Starters are curated, tested collections of compatible libraries — adding spring-boot-starter-web brings in Tomcat, Spring MVC, and Jackson automatically. Always remove the version tag from starters and let spring-boot-starter-parent manage versioning, preventing conflicts and simplifying upgrades.

What is a bean in Spring Boot?

A bean is a regular Java object created, wired, and managed by Spring's IoC (Inversion of Control) container. You declare what should be a bean using stereotype annotations — @Service for business logic, @Repository for data access, @Controller for web handlers, @Component for utilities — and Spring handles instantiation, dependency wiring, and lifecycle automatically.

Why should I remove the version tag from Spring Boot starter dependencies?

Removing the version tag lets spring-boot-starter-parent manage it for you. The parent POM inherits from spring-boot-dependencies, which declares tested, compatible versions for all ecosystem libraries. Keeping explicit versions bypasses this and risks incompatible combinations. When you upgrade Spring Boot's version in the parent tag, correct versions cascade to all managed dependencies at once.

How do I program against interfaces in Spring Boot?

Extract an interface from your dependency (using IntelliJ's Refactor → Extract → Interface), then make the dependent class reference the interface type instead of the concrete class. Annotate only the concrete implementation with @Service — never the interface. This lets you swap implementations, like Stripe for PayPal, with zero changes to the dependent class.

What results can I expect from applying this Spring Boot methodology?

You'll produce a decoupled, testable, well-structured Spring Boot application where swapping implementations requires zero changes to dependent classes, dependency versions never conflict, and builds are reproducible across every machine. Missing dependencies fail loudly at startup instead of silently at runtime, and your project layout follows professional conventions from day one.

Why does IntelliJ Community Edition not work for Spring Boot?

IntelliJ Community Edition lacks the built-in Spring Boot project generator and many tools needed for productive Spring development. Mosh's methodology explicitly requires IntelliJ IDEA Ultimate, which includes Spring Boot project generation, built-in Maven, dependency search, and smart code completion — all essential for professional Spring Boot work.

What is the Maven Wrapper and why should I use it?

The Maven Wrapper (mvnw / mvnw.cmd) is a set of shell scripts committed to your project that download and run a specific, pinned Maven version. It ensures identical builds across all machines — local and CI — without requiring a global Maven install, eliminating environment-specific build surprises caused by version mismatches.

// GET THIS SKILL — FREE

Use this skill in your AI

Every skill on SkillForge is free. Drop your email and copy this skill straight into Claude, ChatGPT, or any LLM.

We'll email you when new skills drop. Unsubscribe anytime.