나의 개발 일상 기록
[MSA] 주문 프로젝트_Partner Domain1 본문
PARTNER DOMAIN_Entity, Service
Domain Layer 구현 시 추상화 레벨을 높이는 것이 좋습니다. Domain Layer의 추상화 레벨을 높이는 이유는 추상화 레벨을 높게 가져가게 되면 Domain Layer의 요구사항의 가독성이 높아지며 세세한 구현과 Low Level에 해당하는 기술은 Infrastructure Layer에 위임을 하기 때문에 코드의 변경이나 기술의 변경에 자유로워 집니다. 또한, Domain Layer의 핵심은 Entity와 특정 서비스를 읽으면 전체 도메인의 흐름을 파악할 수 있어야 하기 때문에 Domain Layer로직과 서비스의 역할에 대해서 분리가 이루어져야 합니다.
Entity 구현
1. 객체 관점에서 필수 속성과 메서드를 정의하여야 합니다.
- Setter는 쓰지 않거나 최소화합니다.
Setter는 캡슐화된 도메인과 객체를 깨트리는 주범이 됩니다. Setter 구현을 최소화하는 이유는 더 나아가 클래스의 변경 가능성을 최소화하기 위함입니다. 클래스의 변경 가능성을 최소화한다는 것은 불변 클래스를 사용해야 한다는 것을 의미합니다. 불변 클래스는 인스턴스의 값이 변하지 않기 때문에 가변 객체와 달리 상태변화에 대해서 신경 쓰지 않아도 됩니다. 그러므로 객체의 동작을 예측하기 쉽고, 오류 발생 가능성도 최소화 할 수있습니다.
예를 들어 아래의 Setter가 존재한다면, 이를 사용하는 측에서는 도메인 객체의 상태를 묻고 상태를 변경하는 로직이 필연적으로 생기기 마련입니다. 또한, 유지보수의 어려움과 도메인의 정합성을 유지하기가 어려워집니다.
public void setStatus(Status status){
this.status = status;
}
아래와 같이 의미를 부여한 변경 로직이 유지보수와 도메인 정합성에 도움이 됩니다.
public void deposited(){
if(this.status == Status.CREATED){
this.status = Status.DEPOSITED;
}
}
2. Entity의 PK와 동등하게 사용되는 대체키를 선언합니다.
대체키의 일반적인 정의를 이야기하자면 후보키가 둘 이상일 대 기본키를 제외한 나머지 후보키들을 말합니다. 하지만 여기서의 대체키의 의미는 Entity의 식별자와 동급의 의미를 가지는 추가 식별자라고 말할 수 있습니다. Entity의 PK와 동등하게 사용되는 대체키를 생성하는 이유는 추후 시스템 외부에 파트너 API를 오픈하여 기능을 제공할 때 Entity의 식별자가 아닌 대체키를 사용하여 외부에 오픈하거나 오용되지 않도록 주의하기 위해서입니다.
대체키를 사용할 때에는 MySQL 기준으로 1천만건 이상으로 넘어가기 전까지는 random string으로 사용해도 조회 성능에 크게 이슈가 없지만, 성능을 고려한다면 UUID를 rearranged하여 사용하는 방법을 검토할 수 있습니다. 방법은 다음의 링크에 자세하게 나와있습니다.
https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
Storing UUID Values in MySQL
Karthik Appigatla revisits a post Peter Zaitsev wrote on UUIDs (Universal Unique IDs), rearranging the timestamp and talks about storing UUID Values.
www.percona.com
위의 글을 간략히 요약하자면 UUID의 구조는 58e0a7d7-eebc-11d8-9669-0800200c9a66의 형태를 가지고 있습니다.
'-'를 기준으로 인덱스 번호를 정한다면 1-2-3-4-5 처럼 표현을 할 수 있습니다. 각 인덱스의 순서를 32145 순서로 어느정도 보장받는 수 체계로 변환이 가능합니다.
58e0a7d7-eebc-11d8-9669-0800200c9a66 => 11d8eebc58e0a7d796690800200c9a66
3. AbstractEntity를 확장하여 사용합니다.
JAVA에서 ORM 기술인 JPA를 사용하여 도메인을 관계형 데이터베이스 테이블에 매핑할 때 공통적으로 도메인들이 가지고 있는 필드나 컬럼들이 존재합니다. 대표적으로 생성일자, 수정일자, 식별가 같은 필드 및 컬럼이 있습니다. 도메인마다 공통으로 존재한다는 의미는 결국 코드가 중복된다는 말과 일맥상통합니다. 데이터베이스에서 누가, 언제하였는지 기록을 잘 남겨놓아야 합니다. 데이터의 생성 및 변경 일자의 데이터를 활용할 수 있는 부분은 데이터처리와 운영, CS 대응 및 데이터 기반의 의사 결정을 위한 지표 추출 부분 등에 활용 된다고 합니다.
Entity를 통한 데이터의 등록과 변경 과정에서 해당 일시를 남기는 반복적인 코딩을 막기 위해 JPA에서는 Audit이라는 기능을 제공하고 있습니다. Audit은 감시하다, 감사하다라는 뜻으로 Spring Data JPA에서 시간에 대해서 자동으로 값을 넣어주는 기능입니다. 도메인을 영속성 컨텍스트에 저장하거나 조회를 수행한 후에 update를 하는 경우 매번 시간 데이터를 입력하여 주어야 하는데, audit을 이용하면 자동으로 시간을 매핑하여 데이터베이스의 테이블에 넣어주게 됩니다.

- @MappedSuperclass는 JPA Entity 클래스에서공통 매핑 정보가 필요할 때, 부모 클래스에 속성을 선언하고 상속하여 사용이 가능하게 해주는 annotation입니다.
- @EntityListeners는 이름 그대로 JPA Entity에 특정 이벤트가 발생했을 때의 리스너와 실행 이벤트를 표현하는 annotation입니다.
- @CreationTimestamp 와 @UpdateTimestamp는 데이터 등록과 변경 시점을 기록하는 컬럼의 선언입니다. LocalDateTime을 사용 시 해당 annotation을 자주 사용합니다.
- ZonedDateTime을 사용 시 ZonedDateTime 을 지원하는 @CreationTimestamp 와 @UpdateTimestamp 를 사용합니다.
Service 및 Implements구현
1. Service를 정의하는 과정에서 메서드의 처리와 조회 및 리턴 객체를 별도로 생성합니다.
도메인 로직과 Entity는 프로젝트 전반에 걸쳐서 사용이 됩니다. Layer간의 참조관계를 생각해보면 알 수 있습니다. 도메인 로직의 리턴 값으로 Entity를 그대로 전달한다면, domain layer 외부에서 Entity 내의 도메인 로직이 호출되거나 Entity의 속성을 변경하는 명령어가 실행이 될 수도 있습니다. 이는 도메인 로직을 domain layer에 응집하고자 하는 의도에 맞지 않고, 경우에 따라서 Entity 하위 객체의 로딩 과정에서 lazy initialization exception 등이 발생할 수도 있습니다.
2. 메서드의 네이밍에 대한 관례를 정합니다.
메서드의 네이밍에 대한 관례를 정하는 것은 중요한 작업이라고 생각합니다. 코딩 스타일에서 표준이라는 것은 존재하지 않지만, 동일한 회사 내에서, 최소한 동일한 팀 내부에서는 같은 코딩 스타일을 결정하고 관리하고 유지하는 것이 원활한 소통과 작업 효율을 증가시키는 길이 될 것이다. 가장 중요한 점은 어떤 코딩컨벤션을 선택하느냐가 중요한 것이 아니라, 통일된 기준으로 코드를 작성하는 것이 중요하다고 생각합니다.
<예시>
| getXxxx : 해당 파라미터로 Entity 리턴. 해당 파라미터로 조회 결과가 없다면 Exception 발생 findByXxxx : Optional<T> 리턴 makeXxxx : DB Operation 과 관계없이 메모리 상에서 값을 조합하여 객체를 생성 initEntity : makeXxxx 로 생성된 초기 Entity. DB 에 저장된 객체가 아니므로 Auto Increment 기반의 PK 값이 null 임 |
3. 도메인 로직이 low level 기술에 영향을 받지 않도록 DIP(의존성 역전 원칙)개념을 적용합니다.
DIP의 정의에 대해서 찾아보면 '추상화 레벨이 높은 상위 수준의 모듈이 추상화 레벨이 낮은 하위 모듈에 의존하면 안된다.'라고 나옵니다. 여기에서 상위 수준의 모듈은 도메인의 정책이나 업무 모델을 포함하는 것을 의미하며, 구체적인 세부 기술을 하위 모듈이라고 할 수 있습니다. 상위 수준의 모듈이 하위 수준의 모듈에 의존하는 전통적인 소프트웨어 개발 방식과 달리, 추상화된 상위 모듈에 하위 모듈이 의존하게 만드는 것이 의존성 역전 원칙입니다.
예를 들어, 한 수리점에서 그동안 자동차만을 수리하고 있었습니다.
해당 수리점은 매출을 자동차 수리에만 의존을 해왔었습니다.

그러다 어느날, 오토바이 수리가 인기가 늘어나면서 해당 수리점은 오토바이 수리도 함께 진행하였습니다.
오토바이 수리가 늘어나면서 수리점에는 오토바이 수리에 필요한 도구(멤버변수)가 추가될 것을 예상할 수 있을겁니다.

이처럼 구체화된, 세부사항에 대한 의존은 코드의 변경을 발생시킵니다.

수리점은 탈것 인터페이스에만 의존하고 자동차 수리, 오토바이 수리에는 의존하지 않습니다. 그 이유는 자동차 수리, 오토바이 수리가 탈것에 의존하고 있기 때문입니다.
다시 말해, 하위 모듈이 상위 모듈을 의존함(수리점 -> 탈것 <- 자동차 수리)을 의존성 역전이라고 합니다.
'MSA > MSA 구축' 카테고리의 다른 글
| [MSA] 선물하기 프로젝트_Flow설계 및 검토 (0) | 2021.09.17 |
|---|---|
| [MSA] 주문 프로젝트_OrderDomain (0) | 2021.09.16 |
| [MSA] 주문 프로젝트_ItemDomain (0) | 2021.09.14 |
| [MSA] 주문 프로젝트_Partner Domain2 (0) | 2021.09.13 |
| [MSA] MSA 프로젝트 구조 및 설계 (0) | 2021.09.08 |