Coding Conventions
주석
클래스 위 - Documentation Comments
/** * 영속성 계층에서 리포지토리를 사용하여 단순한 조회로직을 담당하는 컴포넌트를 'Query' 라고 지정한다. * - Query 는 클래스에 적용한다. */ public @interface Query { }
<br>, <pre>, <li>
등의 html 태그는 사용하지 않음
메서드 위 - Block Comments, Single-Line Comments
/* 좋아요 숫자 업데이트 */ void updateLikeCount(long bookmarkId, ReactionType existedType, ReactionType requestType); /* 태그 목록 조회 */ List<GetUsedTagWithCountResult> getUsedTagsWithCount(long profileId);
/** * 리액션 요청 * - 사용자는 북마크에 대해 하나의 reaction 만을 가질 수 있다. * - 사용자는 reaction 을 등록, 취소, 변경 할 수 있다. * like, hate 요청 reactionType -> like, hate * 등록 0 0 like 1 0 * 취소 1 0 like 0 0 * 변경 0 1 like 1 0 */ void requestReaction(ReactionCommand command);
- 인터페이스의 메서드에는 주석 필수, 적절한 한글 네이밍 제공
@Override
메서드의 경우 인터페이스에 주석이 있으므로 생략 가능
- 혹은 더 구체적인 비즈니스 로직에 대한 주석을 Block-Comment 로 작성 가능
코드 내부 - Single-Line Comments
@Override public void requestReaction(ReactionCommand command) { ... /* 리액션 요청 */ final ReactionType existedType = profile.requestReaction(bookmark, requestType); /* 북마크 좋아요 숫자 업데이트 */ bookmarkService.updateLikeCount(bookmarkId, existedType, requestType); }
테스트 코드
//given, //when, //then
의 경우 에만 End-Of-Line Comments 사용
- 그 외에는
/* */
- Single-Line Comments 사용
컨트롤러 계층
- 매핑 메서드 인자 작성 방식 아래와 같이 통일
/* 북마크 등록 */ @PostMapping public Map<String, Object> registerBookmark( final @AuthenticationPrincipal SecurityUser user, final @RequestBody RegisterBookmarkRequest request ) { return Map.of("id", bookmarkService.registerBookmark(request.toCommand(user.getProfileId()))); }
- 하나의 컨트롤러에서 하나의 서비스에만 의존
- 예외) 프로필 사진 등록을 위한 ProfileService의 S3Uploader 의존
- 예외) 프로필 사진에서 태그 서비스, 북마크 서비스 등 의존 - 리팩토링중
- 조회 요청의 경우 서비스에서 조회해온 Result 데이터를 Response 데이터로 변환 하는 중간에 new-line 하나 두기
서비스 계층
@Service
는 항상@Transactional(readonly=true)
와 함께 클래스를 선언한다- CUD 메서드의 경우 메서드에
@Transactional
추가
영속성 계층
- 애그리거트 하나당 하나의 리포지토리를 제공한다.
Domain.entity
- model 로 이름 바꾸고 싶음
- vo 패키지 없애고 싶음
테스트 코드
//given //when //then
주석 사용
- 계층별 테스트 작성시 BaseXXXTest 사용
- e.g.)
BaseControllerTest
,BaseServiceTest
- BaseXXXTest의
한글 셋업 메서드
를 사용해 테스트 데이터 셋업하기 - 검증 대상이 되는 메서드의 호출은 셋업 메서드를 사용하지 않는다.
한글 검증 메서드
를 사용해 테스트 데이터를 검증 할 수 있다.
- 검증 방식
- assertThat/ isEqaulTo를 기본으로 사용
- 가독성을 고려하여 다른 메서드 적절하게 사용
- e.g.)
extracting
시리즈,isTrue
- 검증이 덩어리로 처리 되는 경우 assertAll 사용해 한번에 검증
@Nested
- 테스트 이름의 prefix는
@Nested
클래스와 통일 - depth 는 최대 하나
- @Nested 를 사용하는 테스트의 경우 외부 클래스의
@BeforeEach
에서 데이터 셋업을 하지 않는다. - 중복 코드가 발생하면
setUpInternal
과 같은 방식으로 우회하고 테스트 메서드 별로 클래스 분리를 고려할것
- 테스트 이름 = 테스트_메서드_이름 (+ 테스트_결과) (+ 이유)
- 테스트_메서드_이름
- 서비스 혹은 리포지토리의 인터페이스 간편 이름
- 간편 이름이 없거나 표현이 너무 길어저 메서드 이름이 충분히 메서드의 의도를 드러내는 경우 영문 메서드 명을 그대로 사용
- 테스트 결과가 항상 성공이거나 굳이 필요 없는 경우 생략
- e.g.)
북마크_생성_성공
,북마크_생성_실패_이름이_너무_김
- 반환값의 변수명은 메서드의 앞글자 따기
- e.g.) registedId = bookmakrService.registerBookmark(…)
- 일련의 동일한 타입의 변수/필드를 선언 할때 숫자를 가장 뒤에 붙일 것
- e.g. )
user1Id
X →userId1
O
- 테스트 순서는 성공 먼저, 실패
- 테스트의 전반적인 순서는 프로덕션 코드의 선언 순서와 통일하기
- controller test
mockMvc
검증 시 when, then 절ResultActions
를 추출하여 구분해 작성- doPrint 항상찍기
Common
- final 키워드 - 필요한 곳에 항상 사용
- 컬렉션(e.g.
TagIds
FavoriteCategories
…) 이 아닌 vo의 경우 - enum (e.g.
ReactionType
,OpenType
,Category
, …) - 별도의 table로 구분되지 않고 사용되는 필드 vo (e.g.
Email
,Link
) - 전역적으로 사용한다 (Controller Layer - Service Layer - Persistence Layer)
- 예외 처리
- 버그 및 공격성 예외
- →
LinkoceanRuntimeException
+ 예외 메시지 발생 - →
CustomRestControllerAdvice.handleBadRequestException( )
- → 400 BadRequest + 메시지 무시
- 입력 예외
- →
IllegalArgumentException
+ 예외 메시지 발생 - →
CustomRestControllerAdvice.handleIllegalArgumentException( )
- → 400 BadRequest + 예외 메시지
Namings
도메인
영어 | 한글 |
bookmark | 북마크 |
user | 사용자 |
profile | 프로필 |
linkmetadata | 링크 메타데이터 |
notification | 알림 |
tag | 태그 |
follow | 팔로우 |
favorite | 즐겨찾기 |
비즈니스 용어
- 컨트롤러, 서비스 계층 에서 활용
영어 | 한글 | 컨트롤러 메서드 사용 예 |
register | 등록 | registerBookmark, registerProfile |
getXXXs | 목록 조회 | getMyBookmarks, getAllCetegories, getProfiles |
getDetailed | 상세 조회 | getDetailedBookmark, getDetailedProfile |
update | 수정 | updateBookmark, updateProfile |
remove | 삭제 | removeBookmark |
follow/unfollow | 팔로우/언팔로우 | follow/unfollow |
favorite/unfavorite | 즐겨찾기/ 즐겨찾기 취소 | favorite/unfavorite |
login/loginSuccess | 로그인/로그인 성공 | login/loginSuccess |
share | 공유 | shareNotification |
obtain | 얻기 | getOrSaveLinkmetadataTitle |
영송성 계층의 용어
영어 | 한글 | 리포지토리 메서드 사용 예 |
find | 조회 | findByProfile_idAndBookmark |
save | 저장 | save |
update | 수정 | update |
delete | 삭제 | deleteByProfileAndBookmarkAndType |
exists | 존재 확인 | existsByOwner_idAndBookmark |
count | 카운트 조회 | countReactionGroup |
XXXInternal
, XXX
- 항상 쌍으로 제공 되어야 하며 XXXInternal은 XXX를 위한 로직을 Jpa QueryMethod 혹은 Jpql을 사용해 제공한다. XXX는 XXXInternal을 사용해 default 메서드로 간단한 변환작업만을 수행하여 제공한다.
findXXXFetchYYYAndZZZ
- 애그리거트 루트 XXX를 조회하며 연관 YYY, ZZZ를 fetch 조인 한다
@Query
- 영속성 계층에서 리포지토리를 사용하여 단순한 조회로직을 담당하는 컴포넌트를 'Query' 라고 지정한다.
- 리포지토리를 사용한 조회가 상대적으로 단순하지만 재활용이 많이 되는 경우 Query 를 활용한다
- Find,Check + [도메인] + [Query]
- e.g.
FindBookmarkByIdQuery
,CheckIsFollowQuery
- 리포지토리를 사용한 조회가 복잡하여 로직을 분리 시키는 것이 적절하다고 생각될때 Query 를 활용한다.
- [도메인] + [Query]
- e.g.) ,
ReactionQuery
DTO
컨트롤러 계층
요청(Request)
[비즈니스 용어] + [도메인] + [Request]
- 비즈니스 용어, 도메인 만으로 구분되지 않는 경우
컨트롤러 메서드 이름 + [Request]
응답(Response)
[도메인] + [Response]
- 도메인 만으로 응답이 구분되지 않는경우
컨트롤러 메서드 이름 + [Response]
서비스 계층
명령 (Command)
[비즈니스 용어] + [도메인] + [Command]
- 데이터의 변경을 요구하는 경우 사용
결과 (Result)
[비즈니스 용어] + [도메인] + [Result]
- 조회성 요청에 대한 응답으로 사용한다
영속성 계층
조건 (Cond - Condition)
- 조회의 필터링 조건
- 서비스 계층에서도 활용가능 하다
- e.g.)
BookmarkFindCond
,ProfileFindCond
페이저블 (pageable)
- Spring 의
PageRequest
사용
- 컨트롤러 계층에서도 활용 가능 하다
페이지 (Page)
- Spring 의 PageImpl 사용
- 서비스 계층에서도 활용 가능 하다
Common
- 메서드 명
checkXXX
: void를 반환하며 인자에 대한 검증으로 사용 - 예외 터트림- e.g.)
Preconditions.checkNotNull()
,Preconditions.checkArgument()
- 값 객체의 필드명
- 단일 필드인 경우
value
- 컬렉션 필드인 경우
values
사용
Architecture Rules
계층형 구조
프로필/북마크 조회 시 join 처리
같은 애그리거트 toMany | 루트 엔티티에서 참조 방식 | 조회 데이터 매핑 |
@Column profile → favoriteIds | Set<Vo> (id = bookmarkId) | 페치 조인 |
@EmbeddedId profile → followIds | Set<Vo> (id = followerId + followeeId) | 페치 조인 |
@Column bookmark → reaction | Set<Vo> (profileId + ReactionType) | 페치 조인 |
@Column profile → favorieCategories | Set<Vo> (Category) | 조회 될 일 없음 ?? |
다른 애그리거트 toOne | 루트 엔티티에서 참조 방식 | 조회 데이터 매핑 |
@ManyToOne bookmark → profile (writer) | 엔티티 - Profile writer | 페치 조인 |
@Column bookmark → linkMetadata | 아이디 - Long linkMetadataId | 서비스에서 말아줌 |
@Column bookmarkTag → tag | 아이디 - long tagId | 서비스에서 말아줌 |
도메인 서비스가 필요한 기능 (여러 애그리거트에 걸친 CUD 요청)
- 현재 도메인 서비스로 동작 중인 기능 (계층 구분은 안 되어 있음)
- 프로필 등록 - 사용자 테이블에 외래키 물려주기
- 북마크 등록/수정 - 태그 테이블에 데이터 추가
TODO
- 북마크 상세
조회
- 북마크 조회 기록 테이블에 데이터 추가/변경 - 북마크 수정/삭제 - 즐겨찾기 테이블에 데이터 삭제
→
이벤트 처리
가 적절할 듯?