Master Java Refactoring: DTO Conversions, Lombok, and Design Patterns
This article explores practical Java backend techniques, including IDE selection, domain vs entity naming, DTO conversion strategies with BeanUtils and custom interfaces, validation with JSR‑303, Lombok shortcuts, builder patterns, static constructors, and design‑pattern considerations, offering concrete code examples and refactoring tips for seasoned developers.
Introduction
The author reflects on eight years of Java development experience and promises a practical, no‑fluff guide for Java programmers seeking to improve their coding style and architecture.
Choosing an IDE
Old habits keep some developers on Eclipse, but the article strongly recommends switching to IntelliJ IDEA for better performance and productivity.
Domain vs Entity Naming
Although many developers name packages com.xxx.domain to mirror database tables, the article argues that these classes are actually entities, not true domain objects, and suggests using com.xxx.entity instead.
DTO Conversion
Data Transfer Objects (DTOs) should be converted to business objects (BO) or entities using utilities like BeanUtils.copyProperties. The article introduces a generic DTOConvert<S,T> interface and concrete implementations for UserInputDTO and UserDTO, demonstrating both forward and backward conversion, and emphasizing semantic method extraction.
@RequestMapping("/v1/api/user")
@RestController
public class UserApi {
@Autowired
private UserService userService;
@PostMapping
public User addUser(UserInputDTO userInputDTO) {
User user = new UserInputDTOConvert().convert(userInputDTO);
return userService.addUser(user);
}
} public interface DTOConvert<S,T> {
T convert(S s);
} public class UserInputDTOConvert implements DTOConvert<UserInputDTO,User> {
@Override
public User convert(UserInputDTO dto) {
User user = new User();
BeanUtils.copyProperties(dto, user);
return user;
}
}Validation with JSR‑303
DTO fields should be annotated with @NotNull and validated using @Valid and BindingResult. Errors are transformed into API‑level exceptions with proper error codes.
public UserDTO addUser(@Valid UserDTO userDTO, BindingResult bindingResult) {
checkDTOParams(bindingResult);
User user = userDTO.convertToUser();
User saved = userService.addUser(user);
return userDTO.convertFor(saved);
}
private void checkDTOParams(BindingResult result) {
if (result.hasErrors()) {
// throw custom validation exception
}
}Lombok for Boilerplate Reduction
Using Lombok annotations such as @Getter, @Setter, @Data, @Accessors(chain = true), and @RequiredArgsConstructor(staticName = "of") eliminates verbose getters, setters, and constructors, and enables fluent, chainable object creation.
@Accessors(chain = true)
@Getter
@Setter
@RequiredArgsConstructor(staticName = "of")
public class Student {
@NonNull private String name;
private int age;
}
Student s = Student.of("zs").setAge(24);Builder Pattern Simplified with Lombok
The classic builder pattern is replaced by Lombok's @Builder, producing concise, readable object construction.
@Builder
public class Student {
private String name;
private int age;
}
Student s = Student.builder().name("zs").age(24).build();Proxy/Decorator Using Lombok @Delegate
Instead of manually delegating every method of RestTemplate, Lombok's @Delegate automatically forwards all RestOperations methods, drastically reducing boilerplate.
@AllArgsConstructor
public abstract class FilterRestTemplate implements RestOperations {
@Delegate
protected volatile RestTemplate restTemplate;
}Time Calculation Example (Joda‑Time)
The article presents a real‑world scenario: determining shipment dates based on order time, using Joda‑Time to handle cut‑off times, next‑day logic, and weekend adjustments.
final DateTime DISTRIBUTION_TIME_SPLIT_TIME = new DateTime().withTime(15,0,0,0);
private Date calculateDistributionTimeByOrderCreateTime(Date orderCreateTime) {
DateTime order = new DateTime(orderCreateTime);
Date tomorrow = order.plusDays(1).toDate();
Date dayAfter = order.plusDays(2).toDate();
return order.isAfter(DISTRIBUTION_TIME_SPLIT_TIME) ? wrapDistributionTime(dayAfter) : wrapDistributionTime(tomorrow);
}
private Date wrapDistributionTime(Date distribution) {
DateTime dt = new DateTime(distribution);
boolean isSunday = dt.getDayOfWeek() == DateTimeConstants.SUNDAY;
return isSunday ? dt.plusDays(1).toDate() : dt.toDate();
}Conclusion
The piece emphasizes continuous refactoring, clean code, and leveraging mature libraries (Spring, Lombok, Joda‑Time) to write maintainable, expressive Java backend code.
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.
Java Backend Technology
Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!
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.
