Why Your Spring Boot Code Is Hard to Maintain—and How the Builder Pattern Solves It
The article explains how the Builder pattern in Spring Boot eliminates the telescoping‑constructor and unsafe‑setter problems of complex objects, improves readability, immutability and maintainability, shows hand‑written and Lombok implementations, provides usage examples for DTOs, API responses and queries, and offers best‑practice guidelines.
Problems with traditional object creation
When a Spring Boot domain class such as Product has many fields, constructors become telescoping (e.g.,
new Product(name, desc, price, sku, available, tags, brand, stock, weight, ...)) and the parameter order is hard to remember. Using setters avoids long constructors but leaves objects partially initialized, mutable, and not thread‑safe, which reduces readability and maintainability.
Builder pattern core idea
Split object construction into multiple steps and complete the build with a chained call.
Typical workflow:
Create a Builder instance.
Set desired properties via fluent methods.
Call build().
Receive the fully constructed, immutable object.
Hand‑written Builder implementation
//src/main/java/com/icoderoad/domain/Product.java
package com.icoderoad.domain;
import java.util.List;
public class Product {
private final String name;
private final String description;
private final double price;
private final String sku;
private final boolean available;
private final List<String> tags;
private Product(Builder builder) {
this.name = builder.name;
this.description = builder.description;
this.price = builder.price;
this.sku = builder.sku;
this.available = builder.available;
this.tags = builder.tags;
}
public static class Builder {
private String name;
private String description;
private double price;
private String sku;
private boolean available;
private List<String> tags;
public Builder name(String name) { this.name = name; return this; }
public Builder description(String description) { this.description = description; return this; }
public Builder price(double price) { this.price = price; return this; }
public Builder sku(String sku) { this.sku = sku; return this; }
public Builder available(boolean available) { this.available = available; return this; }
public Builder tags(List<String> tags) { this.tags = tags; return this; }
public Product build() { return new Product(this); }
}
}Using the hand‑written Builder
Product product = new Product.Builder()
.name("MacBook Pro")
.description("Apple laptop")
.price(1999)
.sku("MBP-2024")
.available(true)
.tags(Arrays.asList("laptop", "apple"))
.build();Builder with Lombok
Add Lombok to the project:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.32</version>
</dependency>Rewrite the class using Lombok annotations:
//src/main/java/com/icoderoad/domain/Product.java
package com.icoderoad.domain;
import lombok.Builder;
import lombok.Getter;
import java.util.List;
@Getter
@Builder
public class Product {
private String name;
private String description;
private double price;
private String sku;
private boolean available;
private List<String> tags;
}Create an instance:
Product product = Product.builder()
.name("MacBook")
.price(1999)
.sku("MBP-2024")
.available(true)
.build();Code size comparison
Hand‑written Builder: >80 lines of code.
Lombok Builder: ~10 lines of code.
Development effort reduced by more than 80%.
Typical Builder use cases in Spring Boot
DTO construction:
ProductDTO dto = ProductDTO.builder()
.name(product.getName())
.price(product.getPrice())
.build();API response wrapping:
ApiResponse response = ApiResponse.builder()
.code(200)
.message("success")
.data(product)
.build();Complex query criteria:
ProductQuery query = ProductQuery.builder()
.keyword("iphone")
.minPrice(500)
.maxPrice(2000)
.build();Best practices for Builder in enterprise Spring Boot projects
Prefer immutable objects: declare fields as private final to guarantee thread safety.
Apply when a class has four or more fields ( field >= 4).
Strongly recommend for DTO/VO, request objects, response objects, and query criteria.
Combine Lombok annotations @Builder, @Getter, and @AllArgsConstructor for concise code.
Avoid Builder for simple objects with only two fields; a regular constructor is sufficient.
Builder vs. traditional construction
Constructor: poor readability, safe, not extensible.
Setter: poor readability, unsafe, extensible.
Builder: good readability, safe, extensible.
Recommended project structure
src/main/java/com/icoderoad
├── controller
├── service
├── repository
├── domain
├── dto
├── query
└── commonBuilders are typically placed in domain, dto, query, and common packages.
Conclusion
The Builder pattern provides a clear, readable, and immutable way to construct complex objects, reducing boilerplate and improving maintainability in large‑scale Spring Boot systems.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
LuTiao Programming
LuTiao Programming is a friendly community offering free programming lessons. We inspire learners to explore new ideas and technologies and quickly acquire job-ready skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
