Builder Pattern in a Spring Boot Project

📘 Premium Read: Access my best content on Medium member-only articles — deep dives into Java, Spring Boot, Microservices, backend architecture, interview preparation, career advice, and industry-standard best practices.

🎓 Top 15 Udemy Courses (80-90% Discount): My Udemy Courses - Ramesh Fadatare — All my Udemy courses are real-time and project oriented courses.

▶️ Subscribe to My YouTube Channel (176K+ subscribers): Java Guides on YouTube

▶️ For AI, ChatGPT, Web, Tech, and Generative AI, subscribe to another channel: Ramesh Fadatare on YouTube

As a Java developer working on real-world backend systems, you’ve probably created objects with multiple fields — some required, some optional.

Maybe you’ve seen classes like this:

new User("John", "john@example.com", null, null, null);

Not only is this hard to read, it’s easy to mess up the order of parameters or forget which field is which.

This is where the Builder Pattern is useful.

In this article, we’ll look at:

  • What the Builder Pattern is (without theory overload)
  • Why it’s useful in real Spring Boot projects
  • 2 practical examples: building a response DTO and a complex configuration object
  • Best practices for using builders in day-to-day backend work

Let’s keep it simple and focused on what matters in real code.


What Is the Builder Pattern?

The Builder Pattern helps you construct complex objects step-by-step without writing multiple constructors or using long parameter lists.

Instead of this:

new Order("John", "MOBILE", 1, "123 Street", false, "note", null);

You write:

Order order = Order.builder()
    .customerName("John")
    .productType("MOBILE")
    .quantity(1)
    .shippingAddress("123 Street")
    .build();

It’s clean, readable, and safe.


How to Use Builder Pattern in Java

You don’t need to write the boilerplate yourself. Use Lombok.

Add to pom.xml:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.30</version>
    <scope>provided</scope>
</dependency>

Make sure annotation processing is enabled in your IDE.


Example 1: Building Response DTOs (Order Confirmation)

Let’s say you’re working on an e-commerce platform.

When a customer places an order, you need to return an API response with:

  • orderId
  • customerName
  • shippingAddress
  • estimatedDelivery
  • couponApplied (optional)
  • deliveryInstructions (optional)

✅ Create a DTO with Builder

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class OrderResponse {
    private String orderId;
    private String customerName;
    private String shippingAddress;
    private String estimatedDelivery;
    private boolean couponApplied;
    private String deliveryInstructions;
}

✅ Use Builder in Service

public OrderResponse buildResponse(Order order) {
    return OrderResponse.builder()
        .orderId(order.getId())
        .customerName(order.getCustomerName())
        .shippingAddress(order.getShippingAddress())
        .estimatedDelivery("3-5 business days")
        .couponApplied(order.hasCoupon())
        .deliveryInstructions(order.getDeliveryNote())
        .build();
}

✅ Controller Usage

@PostMapping("/checkout")
public ResponseEntity<OrderResponse> checkout(@RequestBody OrderRequest request) {
    Order order = orderService.placeOrder(request);
    OrderResponse response = orderService.buildResponse(order);
    return ResponseEntity.ok(response);
}

Why Use Builder Here?

Benefit How It Helps
Optional fields Don’t need multiple constructors
Readable Easy to see which value is being set
Safe Avoids mistakes with parameter ordering
Flexible You can skip fields without errors

In real-world APIs, response payloads often have many optional values. Using builders keeps your code clean.


Example 2: Building Configuration Objects (Notification Settings)

Imagine you're writing a feature to send notifications to users. You want to let different modules configure how notifications are sent.

You might have a config object like this:

  • channel (email, sms, push)
  • retryCount (default: 3)
  • priority (default: normal)
  • delaySeconds (optional)
  • failSilently (optional)

✅ Define Config with Builder

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class NotificationConfig {
    private String channel;
    private int retryCount;
    private String priority;
    private Integer delaySeconds;
    private boolean failSilently;
}

✅ Use It in Notification Service

@Service
public class NotificationService {

    public void send(String to, String message, NotificationConfig config) {
        System.out.println("Sending via: " + config.getChannel());
        System.out.println("Priority: " + config.getPriority());
        System.out.println("Retries: " + config.getRetryCount());
        // Simulate actual send logic
    }
}

✅ Build Config Based on Context

NotificationConfig config = NotificationConfig.builder()
    .channel("email")
    .priority("high")
    .retryCount(5)
    .failSilently(true)
    .build();

notificationService.send("user@example.com", "Welcome!", config);

Now, different modules (order, billing, alerts) can configure notifications however they want — no need to overload constructors or use long method params.


💡 Why Use Builder Here?

  • Lets you create config once and pass it around
  • Supports default + optional values easily
  • Keeps the send() method simple and focused
  • Encourages clear setup logic per feature

Common Use Cases in Spring Boot Projects

You’ll see the Builder Pattern used in many parts of a real app:

Use Case Example
API Response Objects UserResponse, OrderSummary, PaymentStatus
Request Payloads SearchRequest, FilterOptions
Config Objects NotificationConfig, CacheSettings, ReportOptions
Logging/Tracing LogEvent.builder().context("checkout").type("error").build()
Domain Aggregates When combining multiple objects into one result

Tips for Using Builder Pattern in Java Projects

  1. Use Lombok – No need to write builder code manually
  2. Mark classes with @Builder and @Data for simplicity
  3. Keep field names clear – builder methods are based on field names
  4. Use builder in tests – great for setting up test data quickly
  5. Avoid mixing builder + setters – use one style for consistency

⚠️ When Not to Use Builder

  • For very simple classes with 1–2 fields
  • If object is truly immutable and doesn’t have optional fields
  • If you don’t control the class (e.g. from third-party library)

In these cases, a constructor or a simple setter might be fine.


Summary

Pattern Builder
Use When You need to build objects with many fields (some optional)
Pros Clean, readable, safe, avoids constructor overload
In Spring Boot Perfect for DTOs, config objects, test data
Tool Lombok makes it super easy to use

Final Thoughts

The Builder Pattern is one of the most practical design patterns for backend developers — especially when you're working with APIs, configuration objects, or complex domain data.

In Spring Boot projects, builders help you:

  • Keep your service and controller layers clean
  • Avoid brittle constructors
  • Make your code easier to read and maintain

If you’ve ever passed null, null, null into a constructor — it’s time to start using builders.

Comments

Spring Boot 3 Paid Course Published for Free
on my Java Guides YouTube Channel

Subscribe to my YouTube Channel (165K+ subscribers):
Java Guides Channel

Top 10 My Udemy Courses with Huge Discount:
Udemy Courses - Ramesh Fadatare