I care a lot about object validation — it keeps bugs out of the system and makes reasoning about code so much easier. Where and how you validate, though, depends on what you’re building: should the object be immutable, will an ORM like Hibernate manage it, or are you following a functional style? In this post I’ll walk through the trade-offs I usually consider: constructor validation, immutability, and a few alternative approaches that have worked well for me.

Funny story: I once spent an entire afternoon chasing a subtle bug that turned out to be an empty string sneaking into a value object. After that, I’m biased toward validating early.

Constructor Validation: Enforcing Correctness at Creation

One straightforward way I make sure objects are valid is to check inputs right inside the constructor. The idea is simple: if an object can’t be created in a valid state, don’t create it. Here’s a compact example I often use:

public class UserRequest {
    private final String username;

    public UserRequest(String username) {
        if (username == null || username.length() < 3) {
            throw new IllegalArgumentException("Username must be at least 3 characters long.");
        }
        this.username = username;
    }

    public String getUsername() { return username; }
}

Advantages of Constructor Validation

  • It prevents invalid objects from existing — which means fewer defensive checks scattered around your code.
  • Validation logic stays inside the class where it belongs.
  • It plays nicely with immutable objects: if construction enforces correctness, the instance can be trusted afterwards.

⚠️ Challenges with Constructor Validation

  1. ORM and dependency-injection frameworks (Hibernate, JPA, etc.) often need a no-arg constructor. That can mean your validation gets bypassed during reflection-based construction.
  2. Serialization/deserialization (Jackson, etc.) sometimes populate fields without going through your constructor, so watch out.
  3. For very large objects, doing heavy validation at construction can add measurable overhead.
  4. The builder pattern complicates constructor validation — builders assemble state in steps, so enforcing everything in a single constructor isn’t always practical.

Alternative: Static Factory Methods

If you want flexibility but still want validation, a static factory method is a neat pattern I’ve used many times:

public class UserRequest {
    private final String username;

    private UserRequest(String username) {
        this.username = username;
    }

    public static UserRequest create(String username) {
        if (username == null || username.length() < 3) {
            throw new IllegalArgumentException("Username must be at least 3 characters long.");
        }
        return new UserRequest(username);
    }
}

Advantages of Static Factory Methods

  • Lets you validate inputs before constructing the object.
  • You can return cached instances or different implementations depending on input.
  • Often easier to integrate with frameworks that have strict construction requirements.

The Role of Immutability in Object Validation

I like immutability for value objects — once created, they don’t change, and that gives you strong guarantees. That said, immutability isn’t always practical, especially for ORM-managed entities.

Immutable Object Example

public class Address {
    private final String city;
    private final String street;

    public Address(String city, String street) {
        if (city == null || street == null) {
            throw new IllegalArgumentException("City and street cannot be null.");
        }
        this.city = city;
        this.street = street;
    }

    public String getCity() { return city; }
    public String getStreet() { return street; }
}

When to Use Immutability

  • Value objects that don’t change (money, coordinates, etc.).
  • Log entries or audit records where history should be preserved.
  • Functional-style code where you want to avoid side effects.

When Immutability Is Impractical

  • ORM-managed entities that require change tracking.
  • Objects that are updated frequently in-place.
  • Complex constructions that are naturally implemented with builders.

Best Practice: Combining Mutable Entities with Immutable Value Objects

In practice I often pick a hybrid approach: keep entities mutable where the framework needs it, but make the value objects they contain immutable. It reduces accidental state changes while staying compatible with ORMs.

Example: Mutable Entity with Immutable Value Object

@Embeddable
public class Address {
    private final String city;
    private final String street;

    protected Address() {} // Required for JPA

    public Address(String city, String street) {
        this.city = city;
        this.street = street;
    }
}

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String username;

    @Embedded
    private Address address; // Immutable

    protected User() {}

    public User(String username, Address address) {
        this.username = username;
        this.address = address;
    }

    public void updateUsername(String newUsername) {
        this.username = newUsername;
    }
}

Why This Works

  • The entity remains easy for the ORM to manage.
  • Embedded immutable value objects (like Address) prevent accidental mutations and simplify reasoning about data.

Wrap-up

There isn’t a single right answer — only trade-offs. My simple checklist when I decide:

  • If I need a rock-solid, always-valid instance, validate early (constructor or factory).
  • If the framework or serializers get in the way, prefer factories or builders with validation.
  • Make value objects immutable whenever it makes sense; keep entities mutable when the persistence layer requires it.

Hopefully these notes help you make pragmatic choices in your codebase. If you’d like, I can take a specific class from your project and sketch a safe, tested implementation using one of these patterns. Happy to help with a concrete example!