2022-09-15-TIL
2022-09-15-TIL
Today I Learned
Go Application Architecture
Go는 프레임워크가 디렉터리 구조를 정해 주지 않는다. 그래서 Go로 웹 애플리케이션을 만들 때 패키지를 어떻게 나눌지를 먼저 찾아봤다.
공식 문서의 출발점은 단순하다. 작은 패키지나 명령은 모듈 루트에 파일을 그대로 두고 시작하고, 커지면 그때 나눈다. 외부에 공개할 생각이 없는 패키지는 internal/ 아래에 둔다. Go 툴체인은 internal 아래의 패키지를 다른 모듈이 import하지 못하게 막기 때문에, 공개 API를 의도치 않게 넓히지 않을 수 있다. 실행 파일이 여러 개라면 cmd/ 아래에 명령마다 디렉터리를 둔다(Organizing a Go module).
go-clean-arch는 Robert C. Martin의 클린 아키텍처를 Go로 옮겨 본 예제 저장소다. 클린 아키텍처의 핵심은 의존성 규칙이다.
“Source code dependencies can only point inwards.”
소스 코드의 의존성은 안쪽(엔티티, 유스케이스)으로만 향해야 한다는 뜻이다(The Clean Architecture). 저장소는 이를 Models(도메인), Repository, Usecase, Delivery 네 층으로 나눈다. HTTP 핸들러 같은 Delivery는 Usecase를 호출하고, Usecase는 Repository 인터페이스에만 의존한다. Usecase가 Repository 인터페이스만 알고 있으므로, 테스트에서는 실제 DB 대신 가짜 구현을 넣을 수 있다. Go에서는 구현체가 인터페이스를 따로 선언하지 않아도 메서드 집합만 맞으면 그 인터페이스를 구현한 것으로 보기 때문에 이런 교체가 가볍다(The Go Programming Language Specification: Interface types).
두 번째 글은 DI 컨테이너를 쓸지에 대한 내용이다. Go에서는 Wire나 Dig 같은 도구가 있지만, 글은 처음에는 컨테이너 없이 생성 함수(팩토리)로 의존성을 직접 조립하라고 권한다. Wire는 런타임 리플렉션 대신 코드 생성으로 의존성 연결 코드를 만들어 주는 도구라서, 손으로 쓴 초기화 코드와 결과물이 크게 다르지 않다(google/wire).
1
2
3
4
5
6
func main() {
db := openDB()
repo := mysql.NewArticleRepository(db)
svc := article.NewService(repo)
rest.NewArticleHandler(e, svc)
}
이렇게 main에서 생성자를 순서대로 호출하는 것만으로도 의존성 방향이 코드에 그대로 드러난다.
- https://github.com/bxcodec/go-clean-arch
- https://brainhub.eu/library/go-web-app-start#is-it-worth-using-a-di-container-and-if-so-which-one
댓글
아직 댓글이 없습니다