저번 달까지 내가 짯던 코드 형식이다. 참고로 기존 실무 코드는 헥사고날 아키텍처가 아니라 레이어드 아키텍처로 되어있다.
type (
someService struct {
someGrpcHandler
someMessageQueue
someRepository
}
SomeService interface {
}
)
func (s *someService) DoSomething() {
someGrpcHandler.DoSomeGrpc()
someMessageQueue.DoSomeMessageQueue()
doSomethingImportant()//비즈니스 로직
someRepository.DoSomeRepository()
}
이 코드의 문제는 서비스의 퍼블릭 메서드를 테스트하기 어렵다는 것에 있다.
모놀리식 아키텍처라면 딱히 겪지 않을 것 같은 문제다. 하지만 MSA는 서비스 간 통신이라는 복잡함이 있다.
위 서비스의 단위 테스트를 작성하고자 해도 매우 복잡한 코드가 나온다.
왜냐하면 grpc, mq, repo 단에서의 코드를 mocking해야하기 때문이다. 한 번 그렇게 짜보니까 매우 지저분하며, 유지보수하기 매우 어렵다는 것을 깨달았다.
근데 내가 테스트하고 싶은 것은 private method인 doSomethingImportant()라는 비즈니스 로직이다. 이것만 테스트하는 데 왜 다른 걸 모킹하고 있지라는 짜증남이 치솟았다. 그래서 private method에 관한 테스트 방법도 찾아봤다. 검색해보니 private method는 테스트를 해선 안되고, 그걸 테스트하고 앉아있는건 니 설계가 거지같다는 것을 알게 되었다.
또 검색하다보니 DDD layerd 아키텍처라는 것을 오랜만에 보게 됬는데, UI->Service->DataAccess
의 3 계층 방식 말고 UI->Service->Domain->DataAccess
의 4계층 방식도 있는 걸 알게 됬다.
UI,DataAccess는 뭐 똑같으니 냅두고, 검색 내용을 좀 참고해서 리팩토링했다. Service는 트랜잭션, 외부와의 통신(grpc, message queue), 로직 전체 흐름을 담당하도록 했다. 그리고 Domain은 그런거 다 신경 안쓰고 순수한 비즈니스 로직만 담당하게 코딩했다. 그리고 Domain은 Repository를 가질 수도 있고 아닐 수도 있게 했다. 이미지 좀 찾아보니, 엄격하게 UI->Service->Domain->DataAccess
로 되어 있는 게 아니라 Service->DataAccess
나 Service->Domain->DataAccess
등의 경로들로 되어 있는 것 같다. 흠...
type (
someService struct {
someGrpcHandler
someMessageQueue
someRepository1
someDomain
}
SomeService interface {
}
)
func (s *someService) DoSomething() {
someGrpcHandler.DoSomeGrpc()
someMessageQueue.DoSomeMessageQueue()
someDomain.DoSomethingImportant()//비즈니스 로직
someRepository1.DoSomeRepository()
}
type (
someDomain struct {
someRepository2
}
someDomain interface {
}
)
func (s *someDomain) DoSomethingImportant() {
//핵심 비즈니스 로직 수행!
...
someRepository2.DoSomeRepository()
}
이렇게 하면서 좋은 거는 서비스의 private method가 아닌 도메인의 public method가 됨으로써 단위 테스트하기 쉬워졌다는 것이다.
솔직히 grpc, message queue 등 외부 요소는 단위 테스트하기도 어렵다. 이러한 요소는 통합 테스트에서 확인해야 할 일이다.
이렇게 도메인 로직만 단위 테스트를 하게 되면 핵심 비즈니스 로직은 좀 더 안전하게 지킬 수 있게 된다. 왜냐하면 뭔가 잘못 건드려도, 작성했던 도메인 단위테스트에서 문제에서 생기지만 않는다면 핵심 비즈니스 로직은 안전함이 꽤 보장되기 때문이다.
이는 리팩토링에 있어서 좀더 안정감을 준다. 그리고 기획 변경에 따른 추가 유지보수가 발생하더라도, 기존 비즈니스 로직이 동일하게 수행됨을 보장할 수 있기 때문에 테스트의 부담감과 시간도 덜해질거다.
https://dev-coco.tistory.com/166
나중에 DDD 책 좀 사서 읽어봐야겟넹