Is it anti-pattern to use setter for Dto?

Viewed 29

Is it anti-pattern to use setter for Dto?

I am working on putting a value in dto after working on business logic in the service layer.

I heard in a blog post that putting setters in dto is anti-pattern.

Is it really anti-pattern to use setters for Dtos?

So what's the alternative?

1 Answers

Not sure if it is an anti pattern, but if you want an immutable DTO and have lots of properties then setting via constructor seems unreasonable and ugly. Could use a nested Builder class to set the properties and expose the builder through a public method, but seems like over-engineering unless there is a compelling reason to not have setter methods on the DTO.

Thus, unless there is a business requirement for the class to be immutable, just use setters on the DTO.

otherwise, take a look at this over-engineered solution with a trivial domain class.

The domain model class...

public class User {
    private int id;
    private String username;
    private String password;

    // getters and setters

The DTO with a nested Builder...

public class UserDto {
    private int id;
    private String username;

    public int getId() {
        return id;
    }

    public String getUsername() {
        return username;
    }

    public static Builder fromBuilder() {
        return new Builder();
    }

    static class Builder {
        private final UserDto user;

        private Builder() {
            user = new UserDto();
        }

        public Builder withId(int id) {
            user.id = id;
            return this;
        }

        public Builder withUsername(String username) {
            user.username = username;
            return this;
        }

        public UserDto build() {
            return user;
        }
    }
}

The mapper...

public class UserMapper {

    public UserDto toUserDto(User user) {
        return UserDto.fromBuilder()
                .withId(user.getId())
                .withUsername(user.getUsername())
                .build();
    }
}

A simple test...

class UserDtoTest {

    UserMapper mapper = new UserMapper();

    @Test
    void shouldBuild() {
        User user = new User();
        user.setId(1234);
        user.setUsername("user1234");
        user.setPassword("super_secret_password");

        UserDto dto = mapper.toUserDto(user);

        assertEquals(user.getId(), dto.getId());
        assertEquals(user.getUsername(), dto.getUsername());
    }

}
Related