[v4] 조금 다른 예를 들어보겠습니다.
/*
1. 제품을 만들기 위한
부품 (인자로서 사용됨을 표시하기 위해 Dto 표기)
*/
@Setter
@Getter
@NoArgsConstructor
public class PartDto {
// 모든 곳에서 필요합니다.
private Boolean steal;
// 컴퓨터와 핸드폰에서 필요합니다.
private Boolean semiconductor;
// 선풍기에서 필요합니다.
private Boolean propellers;
// 선풍기와 핸드폰에서 필요합니다.
private Boolean button;
}
/*
1. 제품을 만들기 위한 부품이 있는지 확인하고
있으면 만드는 서비스
*/
public class ProductService {
public boolean createComputer(PartDto partDto) {
if (partDto.getSteal() && partDto.getSemiconductor()) {
// do work...
return true;
}
return false;
}
public boolean createFan(PartDto partDto) {
if (partDto.getSteal() && partDto.getPropellers() && partDto.getButton()) {
// do work...
return true;
}
return false;
}
public boolean createPhone(PartDto partDto) {
if (partDto.getSteal() && partDto.getSemiconductor() && partDto.getButton()) {
// do work...
return true;
}
return false;
}
}
으악! 요즘 누가 이렇게 코드를 짜나!
하지만 종종 이런 코드를 보곤 하죠
이상적인 프로그래밍과는 조금 거리가 있지만 Dto를 공통적으로 쓰고, Dto 생성 후 부분적으로 Setter 사용, (전 글에서 나온 것과 같이) 메시지 인자를 수신받는 곳에서 검증합니다. 만약 제품이 늘어난다면 검증 로직을 어디에 만들어야 될까요?
[v5] 저는 검증 로직을 모으는 것을 선호합니다.
/*
1. 제품을 만들기 위한
부품 (인자로서 사용됨을 표시하기 위해 Dto 표기)
*/
@Setter
@Getter
@NoArgsConstructor
public class PartDto {
public enum PartOf {
COMPUTER,
FAN,
PHONE,
UNKNOWN
}
private PartDto.PartOf partOf;
...
public boolean naming(PartDto.PartOf partOf) {
if (partOf.equals(PartOf.COMPUTER) && this.getSteal() && this.getSemiconductor()) {
this.partOf = PartOf.COMPUTER;
}
if (partOf.equals(PartOf.FAN) && this.getSteal() && this.getPropellers() && this.getButton()) {
this.partOf = PartOf.FAN;
}
if (partOf.equals(PartOf.PHONE) && this.getSteal() && this.getSemiconductor() && this.getButton()) {
this.partOf = PartOf.PHONE;
}
if (this.partOf == null) {
this.partOf = PartOf.UNKNOWN;
return false;
}
return true;
}
}
/*
1. 제품을 만들기 위한 부품이 있는지 확인하고
있으면 만드는 서비스
*/
public class ProductService {
public boolean create(PartDto partDto) {
// do work...
return true;
}
}
/*
1. 제품을 만드는 서비스를 호출하는
객체
*/
@RequiredArgsConstructor
public class Factory {
private final ProductService productService;
public void createProduct() {
PartDto part1 = new PartDto();
part1.setSteal(true);
part1.setPropellers(true);
part1.setButton(true);
if (part1.naming(PartOf.FAN)) {
System.out.println("part1 로 FAN 만들기: " + productService.create(part1));
}
PartDto part2 = new PartDto();
part2.setSteal(true);
part2.setPropellers(true);
part2.setButton(true);
// 검증에 실패해 제품 생산을 위한 요청 (메시지) 를 보내지 않습니다.
if (part2.naming(PartOf.COMPUTER)) {
System.out.println("part2 로 COMPUTER 만들기: " + productService.create(part2));
}
}
}
특히 Dto로 모으는 것을 선호합니다.
1. 객체에 역할이 있는 것처럼, 메시지도 역할을 줄 수 있다.
2. 메시지를 보내는 것도 비용이다. 사전에 검증되어 전달된 메시지는 신뢰한다. (내부용 로직일시에만)
3. 비슷한 성격의 로직만 모음으로서 생각의 범위를 줄일 수 있다.
물론 다른 해결 방법도 여러 가지가 있습니다.
Dto를 더 분류할 수도 있고, 검증만을 하는 서비스를 만들 수도 있고, 만약 API 호출 형태였다면 수신 측에서 검증하는 전 구조가 더 나았겠죠.
무엇이 되었든 중요한 건 약속된 구조로 레이어를 나누고 팀에 공유하는 것이라고 생각합니다.
목표는 지속 가능한 개발이니까요.
회고
사실 이 글을 쓰게 된 계기는 대가의 이론과 내 능력 사이의 괴리, 거기서 오는 설득력의 부족, 배움이 필요한 기술과 한정된 일정에서 오는 이런저런 안 좋은 생각 중에 문득 눈에 들어와서였습니다. 만약 대가의 이론이기에 문자 그대로를 적용해야 된다는 게 기저에 있다면, 재해석과 설득은 너무 힘든 과정이 됩니다.
다행히도 옛날이었으면 무심코 지나쳐갔을 내용을 코드로 설명할 수 있는 능력이 생겼고, 내용을 정리하는 과정에서 나쁜 생각은 많이 사라진 것 같습니다.
지속 가능한 개발을 유지하되 그 기준을 상향시키자!
회고의 결론은 항상 같은 느낌.
'Study' 카테고리의 다른 글
mongoDB tutorials (0) | 2021.05.18 |
---|---|
API Versioning 전략 (0) | 2021.04.14 |
토끼책을 읽고 - 메시지를 믿을 수 있을까? (1) (0) | 2021.03.08 |
비동기 테스트 만들기 (0) | 2021.02.25 |
Querydsl 로 가는 길 (0) | 2020.12.02 |