도메인과 패키지 구조
어드민을 분리하다가
프로젝트의 어드민 코드를 손보다가 여러 생각이 들었다. 고민했던 흐름대로 글을 한번 써보려고 한다.
고민의 시작
어드민 분리
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
└── infra
└──config
└──logging
└──....
|
└── domain
└── admin
└── community
└── post
└── comment
└── map
└── notice
|
└── presentation
└── admin
└── community
└── map
└── notice
패키지 구조는 대강 이런식으로 잡혀 있었다. presentation -> domain -> infra 와 같은 순서로 의존관계가 구성되게 의도한 구조였는데, 여기서 문득 든 생각은 어드민은 도메인으로 볼수 있는가? 였다.
사람마다 생각은 다를 수 있지만, 내 의도는 하나의 도메인이 서비스의 최소 단위가 될 수 있도록 구성하는 것이었다. 조금 더 쉽게 풀어쓰자면 ‘개별적으로 구성될 수 있는 가장 작은 서비스의 단위’ 정도일 것 같다.
모호해진 경계
내 의도대로 구성이 잘 되었다면, 다른 도메인 코드를 수정해도 영향력이 최소화 되어야 한다. 즉, notice(공지사항) 도메인 코드를 수정해도 map, community와 같은 도메인에는 영향이 없어야 한다. 그런데 notice 도메인과 admin 도메인이 뒤섞이는 일이 자꾸 발생했다.
notice 도메인에는 유저 기능과 관리자 기능이 섞여있었다. 유저는 읽기만 가능하고, 관리자는 공지사항 등록, 수정, 삭제가 가능하다. 그런데 관리자 api를 수정하는데 자꾸 notice 도메인에 영향이 가고 있어서, 도메인을 조금 더 명확히 분리할 필요를 느꼈다.
모듈화 시도
처음 든 생각은 별도의 모듈로 유저 기능과 관리자 기능을 분리하는 것이었다.
아예 별개의 모듈로 분리해놓으면 참조 자체가 불가능하고, 개발할때 혹시 모를 실수를 방지할 수 있을 것이라 생각했다. 그런데 다시 문제가 발생했다.
1
2
3
4
5
6
└── admin-api
└──관리자 기능
|
└── core-api
└── 유저 기능
└── notice Entity
이런식으로 분리하려고 했더니, 어드민쪽에서 notice 엔티티 자체를 참조할 수 없게 되었다. 여기서 또 2가지 정도 생각을 해봤다.
별도 api를 통한 업데이트
첫 번째로 든 생각은 유저 api 모듈에 notice를 업데이트 할 수 있는 api를 만들고, 관리자 api쪽에서는 별도의 클라이언트로 해당 api에 요청을 쏘는 방식이었다.
그런데 이런 방식은 admin을 분리한 보람이 없을 듯 했다. 관리자 기능들을 admin-api에 격리시키기 위해서 모듈을 나누는건데, 이러면 유저 api에 관리자인지 검증하는 코드가 추가되어야 하고, 이걸 방지하기 위해서는 모듈을 더 세세하게 분리해야 할 것 같은데 일이 점점 커지는 것 같아서 일단 접어뒀다.
공유되는 엔티티 별도 관리
1
2
3
4
5
6
7
8
└── admin-api
└──관리자 기능
|
└── core-api
└── 유저 기능
|
└── common-entity
└── entity
두 번째로 든 생각은 엔티티 관련 모듈을 분리하는 것이었다.
이러면 3개의 모듈 구성만으로도 어느정도 격리가 가능할 것이라 봤고, 복잡도도 어느정도 낮게 조절할 수 있지 않을까 생각했다. 그런데 모듈로 분리하는게 생각보다 이것저것 바꿀것도 많고 손댈곳들도 꽤 있어서, 적당히 타협안을 생각해보기로 했다.
1
2
3
4
5
6
7
8
9
10
11
12
└── domain
└── admin
└── notice
└── adminNoticeRepository
└── adminNoticeService
└── ...
└── notice
└── noticeEntity
└── noticeRepository
└── noticeService
└── ...
현재 패키지 구조를 갼락화 해봤다. 먼저 어드민 도메인을 조금 더 세분화했다. 그리고 별도의 레포지토리와 서비스 클래스를 만든 후에, notice 도메인의 엔티티만 참조할 수 있도록 구성했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public interface admin.notice.AdminNoticeRepository extends JpaRepository<NoticeEntity, Long> {
// 어드민 기능들 정의 (생성, 수정, 삭제)
}
...
...
...
public interface notice.NoticeRepository extends JpaRepository<NoticeEntity, Long> {
// 유저 기능들 정의 (조회)
}
public class notice.NoticeEntity {
// ...
}
이런 방식의 코드다. 엔티티만 참조하도록 해서 도메인간 코드 변경의 영향을 최소한으로 받을수 있도록 구성해봤다.
뭐가 최선일까
만약 규모가 큰 서비스라면 어드민을 별도의 프로젝트로 분리하는 것이 좋은 방법일 것 같다. 그런데 나는 어드민 페이지에서 딱히 하는것도 없고, 그렇게 큰 서비스도 아니라서 어느정도 타협해가면서 코드를 작성한 것 같다.
개발에 들이는 리소스와 그에 따른 아웃풋을 생각하는것이 종종 뇌절코드를 작성하는 나를 막아주는 가장 좋은 방법인 것 같다.
참고
- 내 경험