Part 33: Java 14 – Records – Eliminating Boilerplate While Building Better Enterprise Models

Introduction

If Java developers were asked one question for the last twenty years, it would probably be:

“Why do I have to write so much boilerplate code?”

For years, enterprise developers solved this problem in three different ways:

  1. Writing Plain Old Java Objects (POJOs)
  2. Using Lombok annotations
  3. Using Java Records (introduced in modern Java)

Today, almost every Spring Boot application uses one of these approaches.

Understanding when to use each one is far more important than simply learning the Record syntax.

This article explains not only how Records work, but also how they compare with Lombok, and where each approach fits into modern enterprise applications.


Learning Objectives

By the end of this article, you will be able to:

  • Understand why Records were introduced.
  • Compare POJOs, Lombok and Records.
  • Understand immutability.
  • Create constructors and validation.
  • Use Records with Spring Boot.
  • Use Records with Jackson.
  • Design REST APIs using Records.
  • Understand why Records should not replace JPA entities.
  • Decide when Lombok is still the better choice.

The Evolution of Java Data Models

The evolution of Java data modeling can be summarized as:

Java POJO

↓

Lombok

↓

Java Records

Each step reduced boilerplate while improving readability.


Traditional POJO

public class EmployeeDto {

    private final Long id;
    private final String name;
    private final String department;

    public EmployeeDto(Long id,
                       String name,
                       String department) {
        this.id = id;
        this.name = name;
        this.department = department;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public String getDepartment() {
        return department;
    }

    @Override
    public boolean equals(Object obj) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }

    @Override
    public String toString() {
        ...
    }
}

This class contains mostly boilerplate.


Lombok

Lombok dramatically reduced the amount of code developers needed to write.

The same DTO becomes:

@Getter
@AllArgsConstructor
@EqualsAndHashCode
@ToString
public class EmployeeDto {

    private final Long id;
    private final String name;
    private final String department;

}

Or even:

@Data
@AllArgsConstructor
public class EmployeeDto {

    private Long id;
    private String name;
    private String department;

}

Compared to a traditional POJO, Lombok removes a significant amount of repetitive code.

However, it introduces a compile-time dependency through annotation processing.


Java Records

The same DTO using Records:

public record EmployeeDto(

        Long id,

        String name,

        String department) {

}

Three lines.

No annotations.

No generated source code.

No annotation processor.


Comparing the Three Approaches

FeaturePOJOLombokRecord
BoilerplateHighLowMinimal
ConstructorManualGeneratedGenerated
GettersManualGeneratedGenerated
equals()/hashCode()ManualGeneratedGenerated
toString()ManualGeneratedGenerated
MutableYesUsuallyNo
ImmutableManualPossibleYes
Requires Annotation ProcessorNoYesNo
Part of JDKYesNoYes

What Does the Compiler Generate?

For a Record:

public record Customer(

        Long id,

        String name) {

}

The compiler automatically generates:

  • Private final fields
  • Canonical constructor
  • Accessor methods
  • equals()
  • hashCode()
  • toString()

Example:

Customer customer =
        new Customer(1L, "Rahul");

System.out.println(customer.name());

Notice the generated accessor:

customer.name()

instead of

customer.getName()

Records Are Immutable

Records are immutable by design.

Customer customer =
        new Customer(1L, "Rahul");

customer.name = "John";

Compilation error.

Immutability makes Records naturally suitable for DTOs, events, and value objects.


Compact Constructors

Validation remains possible.

public record Customer(

        Long id,

        String name){

    public Customer{

        if(name == null ||

           name.isBlank()){

            throw new IllegalArgumentException(
                    "Name cannot be blank");

        }

    }

}

The compiler performs field assignment automatically after validation.


Additional Methods

Records can contain behavior.

public record Money(

        BigDecimal amount,

        String currency){

    public boolean isPositive(){

        return amount.signum() > 0;

    }

}

Records are not restricted to getters.


Records with Jackson

Modern versions of Jackson support Records directly.

Record

↓

Jackson

↓

JSON

and

JSON

↓

Jackson

↓

Record

No setters or default constructors are required.


Records in Spring Boot

Records work exceptionally well for:

  • Request DTOs
  • Response DTOs
  • Configuration Properties
  • Kafka Events
  • RabbitMQ Messages
  • Immutable Value Objects
  • REST Contracts

Example:

public record CreateCustomerRequest(

        String firstName,

        String lastName,

        String email){

}

Records and JPA

This is one of the biggest misconceptions.

Avoid:

@Entity
public record Customer(...){

}

JPA entities generally require:

  • Mutable state
  • Proxy generation
  • Lazy loading
  • Lifecycle callbacks
  • No-argument constructors

Records intentionally provide:

  • Final components
  • Immutability
  • Canonical constructors

These goals conflict with traditional JPA entity requirements.

Recommended architecture:

Database

↓

JPA Entity

↓

Mapper

↓

Record DTO

↓

REST API

Records vs Lombok

This is the question most enterprise developers ask.

Use Records When

✔ Building REST request DTOs.

✔ Building REST response DTOs.

✔ Creating Kafka or RabbitMQ events.

✔ Creating immutable configuration objects.

✔ Modeling value objects.

✔ Creating API contracts.

Example:

public record OrderResponse(

        UUID orderId,

        BigDecimal amount,

        Instant createdAt){

}

Records express intent clearly: this object is immutable.


Use Lombok When

Lombok is still valuable for scenarios requiring mutability or additional code generation.

Examples include:

  • JPA entities
  • Builder pattern
  • Mutable domain objects
  • Legacy applications
  • Classes requiring many optional fields

Example:

@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Entity
public class Customer {

    @Id
    private Long id;

    private String name;

    private String email;

}

Features such as @Builder, @With, and selective accessor generation remain valuable and are not replaced by Records.


Records vs Lombok Builder

Suppose a class contains fifteen fields.

Creating a Record constructor becomes cumbersome:

new CustomerResponse(
    id,
    name,
    email,
    phone,
    address,
    ...
);

Lombok Builder:

Customer customer =
        Customer.builder()
                .name("Rahul")
                .email("rahul@example.com")
                .build();

For complex object creation, builders are often more readable than long constructors.


Enterprise Decision Matrix

ScenarioRecommended Choice
REST Request DTO✅ Record
REST Response DTO✅ Record
Kafka Event✅ Record
RabbitMQ Event✅ Record
Configuration Properties✅ Record
Immutable Value Object✅ Record
JPA Entity✅ Lombok / Regular Class
Mutable Domain Object✅ Lombok
Builder Pattern✅ Lombok
Legacy Spring Boot Project✅ Lombok
Complex Object Construction✅ Lombok Builder

Common Mistakes

Replacing Every Lombok Class with a Record

Not every class should become a Record.

Choose the abstraction that reflects the lifecycle and mutability of the object.


Using Records for Entities

Entities and Records have fundamentally different design goals.


Assuming Records Replace Lombok

They do not.

Records eliminate much of the need for Lombok in immutable DTOs, but Lombok remains valuable for mutable classes, builders, and many enterprise patterns.


Best Practices

✔ Prefer Records for immutable API contracts.

✔ Continue using Lombok for JPA entities.

✔ Use Lombok Builder for complex object creation.

✔ Validate Record components using compact constructors.

✔ Keep entities and DTOs separate.

✔ Let Records model data, not business workflows.


Interview Questions

Do Records replace Lombok?

No.

Records replace much of Lombok’s usage for immutable DTOs, but Lombok remains useful for builders, mutable objects, and JPA entities.


Should JPA entities be converted to Records?

Generally no.

JPA entities require capabilities that Records intentionally do not provide.


When is Lombok Builder preferable?

When constructing complex objects with many optional fields or when constructor parameter lists become difficult to read.


When should Records be preferred?

For immutable data carriers such as REST DTOs, messaging events, configuration objects, and value objects.


Summary

Records are one of the most important additions to modern Java because they eliminate boilerplate while making immutability the default. They integrate seamlessly with Spring Boot, Jackson, and messaging frameworks, making them an excellent choice for DTOs, API contracts, configuration objects, and events.

However, Records do not replace every Java class. JPA entities, mutable domain models, and complex object construction often remain better served by Lombok or traditional classes. Modern enterprise applications frequently use both technologies:

  • Records for immutable data transfer and messaging.
  • Lombok for entities, builders, and mutable domain models.

Choosing between them is not a matter of which is “better,” but of selecting the right tool for the specific responsibility within your architecture.

Leave a Reply

Your email address will not be published. Required fields are marked *