You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
문제상황 한줄 정리: wallet 모듈은 그 어떤 모듈도 참조할 수 없도록 allowedDependencies 를 설정하였지만, wallet.internal 모듈에서 PaymentApi의 인스턴스 생성하였음에도 modules.verify() 테스트가 통과하는 문제상황 발생
초기 패키지 구조
초기 패키지 구조는 다음과 같았다. Payment 모듈과 Wallet 모듈을 자세히 살펴보자.
payment 모듈의 Root 클래스인 PaymentApi와 wallet 모듈의 Root 클래스인 PayWalletApi는 외부 모듈에게 노출된 Public API이다.
하지만, 앞선 이벤트 스토밍을 통해 정의한 우리 팀의 바운디드 컨텍스트에 의하면, payment는 wallet에 의존할 수 있지만, wallet은 그 어떤 모듈도 바라보지 않도록 설계하였다.
따라서 Spring Modulith의 공식문서의 Explicit Application Module Dependencies 파트를 참고하여, package-info 파일을 생성하여 Payment모듈과 Wallet 모듈의 의존성을 관리하였다.
각 패키지의 package-info 코드는 다음과 같다.
//payment.package-info.kt
@file:ApplicationModule( //@file: 파일 수준(파일에 포함된 모든 코드나 파일 자체)에 어노테이션을 적용한다.
type =ApplicationModule.Type.CLOSED,
allowedDependencies = ["account", "wallet"] //payment 모듈은 account, wallet 모듈에 의존성을 갖는다.
)
packagetech.sipe.fintech.paymentimportorg.springframework.modulith.ApplicationModule
//wallet.package-info.kt
@file:ApplicationModule(
type =ApplicationModule.Type.CLOSED,
allowedDependencies = [] //wallet 모듈은 그 어떤 모듈도 바라보지 않는다.
)
packagetech.sipe.fintech.walletimportorg.springframework.modulith.ApplicationModule
따라서, 위와 같이 정의한 모듈 의존성에 따르면, wallet 모듈 내부에서 payment의 public api(payment 모듈의 루트 클래스)도 참조할 수 없어야 한다고 생각하였다.
따라서, 의존성이 제대로 선언 되었는 지 확인하기 위해, wallet의 internal 패키지에 위치한 PayWalletService에, PaymentApi 인스턴스를 생성한 후, modules.verify() 테스트를 진행하였다.
당연히, PaymentApi는 public으로 선언된 인터페이스이기 때문에 JVM 컴파일 단계에서는 에러가 발생하지 않는다. 따라서 Spring Modulith가 의존성 허용 및 제한을 강제하는지, 위 테스트를 진행하여 확인하고자 하였다.
테스트가 통과해 버렸다..
고민 사항
Spring Modulith가 모듈 별 root에 존재하는 클래스를 외부 모듈에 접근을 허용하는 것은 알고 있었으나, 특정 모듈에 대해 제한하여 접근 가능하도록 설정할 수 있었다고 생각했는데, allowedDependencies는 단지 선언만 해주고 강제하지는 않는 것인가? 까지 고민하였다. 모듈간 의존성을 강제로 제한하지 않는다면, 이 기술을 사용하는 의미가 뭐가 있을까? 하고 다시 공식문서를 살펴보았다. 결론적으로 강제하는 것이 맞고 Kotlin 환경에서의 패키지 메타데이터를 선언하는 방식이 달랐던 것이었다.
A module can opt into declaring its allowed dependencies by using the @ApplicationModule annotation on the package, represented through the package-info.java file. As, for example, Kotlin lacks support for that file, you can also use the annotation on a single type located in the application module’s root package.
Kotlin은 package-info.java 파일을 지원하지 않으므로, 대신 애플리케이션 모듈의 루트 패키지에 위치한 단일 클래스 또는 타입에 해당 어노테이션을 사용할 수도 있다고 작성 되어 있음과 동시에 Kotlin 예제코드도 작성되어 있다.
A place for package-level documentation (패키지 수준 문서 작성시)
Home for package-level annotations (패키지 전체에 영향을 미치는 어노테이션을 선언하는 경우)
따라서, Spring Modulith는 패키지에 적용된 메타데이터를 분석할 때, package-info.java에 선언된 @ApplicationModule을 탐색한다.
하지만, Kotlin은 Java의 package-info.java 파일처럼 패키지 자체에 메타데이터를 정의하는 방식을 지원하지 않기 때문에, package-info.kt라는 파일을 만들어도 특별한 의미를 가지지 않고, 일반 Kotlin 파일과 다를 바가 없다.(즉, Spring Modulith가 모듈의 메타데이터로 인식하지 않는다.)
그래서 Spring Modulith에서는 Kotlin에서 패키지 메타데이터를 정의하기 위해 클래스 수준에서 선언하는 방식을 권장한다. (공식문서에서는 애플리케이션 모듈의 루트 패키지에 위치한 단일 클래스나 타입에 사용할 수 있다고 작성)
즉, moduleMetadata라는 단일 클래스를 생성하여 @ApplicationModule을 사용해야 한다.
해결
//wallet.WalletModuleMetadatapackagetech.sipe.fintech.walletimportorg.springframework.modulith.ApplicationModule
@ApplicationModule(
type =ApplicationModule.Type.CLOSED,
allowedDependencies = []
)
classWalletModuleMetadata
다음과 같이 단일 클래스로 패키지 메타데이터 설정 파일을 작성하였고 해당 클래스에 @ApplicationModule 어노테이션의 옵션 값으로 그 어떤 의존성도 추가하지 않았다.
테스트 실패! → 의존성 설정이 강제 된 것을 확인할 수 있다.
//errorModule'wallet' depends on module 'payment'
via tech.sipe.fintech.wallet.internal.service.PayWalletService-> tech.sipe.fintech.payment.PaymentApi.
Allowed targets: none.
정리
공식문서 잘 읽자
글에 잘못된 내용이 있다면 댓글 달아주시면 감사하겠습니다!
The text was updated successfully, but these errors were encountered:
sangjun121
changed the title
Spring Modulith + Kotlin 환경에서 모듈간 의존성 관리법
⚠️ Spring Modulith + Kotlin 환경에서 모듈간 의존성 관리법
Jan 12, 2025
개요
재영님이 Spring Modulith를 실습하신 과정에서 발생한 문제사항을 금일 모각작에서 해결한 과정을 기술하였습니다😀
문제상황
Originally posted by @jaeyeong951 in #12 (comment)
문제상황 한줄 정리: wallet 모듈은 그 어떤 모듈도 참조할 수 없도록
allowedDependencies
를 설정하였지만, wallet.internal 모듈에서 PaymentApi의 인스턴스 생성하였음에도modules.verify()
테스트가 통과하는 문제상황 발생초기 패키지 구조
초기 패키지 구조는 다음과 같았다. Payment 모듈과 Wallet 모듈을 자세히 살펴보자.
payment 모듈의 Root 클래스인 PaymentApi와 wallet 모듈의 Root 클래스인 PayWalletApi는 외부 모듈에게 노출된 Public API이다.
하지만, 앞선 이벤트 스토밍을 통해 정의한 우리 팀의 바운디드 컨텍스트에 의하면,
payment는 wallet에 의존할 수 있지만, wallet은 그 어떤 모듈도 바라보지 않도록 설계하였다.
따라서 Spring Modulith의 공식문서의 Explicit Application Module Dependencies 파트를 참고하여, package-info 파일을 생성하여 Payment모듈과 Wallet 모듈의 의존성을 관리하였다.
각 패키지의 package-info 코드는 다음과 같다.
따라서, 위와 같이 정의한 모듈 의존성에 따르면, wallet 모듈 내부에서 payment의 public api(payment 모듈의 루트 클래스)도 참조할 수 없어야 한다고 생각하였다.
따라서, 의존성이 제대로 선언 되었는 지 확인하기 위해, wallet의 internal 패키지에 위치한 PayWalletService에, PaymentApi 인스턴스를 생성한 후,
modules.verify()
테스트를 진행하였다.고민 사항
해결 → Kotlin의 package 메타데이터 설정 방식이 java와 달랐음
Spring Modulith의 Explicit Application Module Dependencies를 다시 읽어보았다.
Kotlin은 package-info.java 파일을 지원하지 않으므로, 대신 애플리케이션 모듈의 루트 패키지에 위치한 단일 클래스 또는 타입에 해당 어노테이션을 사용할 수도 있다고 작성 되어 있음과 동시에 Kotlin 예제코드도 작성되어 있다.
package-info.kt로 왜 모듈의 메타데이터를 설정할 수 없었을까?
이 질문을 해결하기 위해, package-info.java 파일에 대해 찾아보았다.
package-info.java 파일은 Java에서 패키지 수준의 정보를 정의하기 위해 사용되는 특별한 파일이다.
java-package-info
package-info.java file을 사용하는 목적은 다음과 같이 2가지가 있다.
따라서, Spring Modulith는 패키지에 적용된 메타데이터를 분석할 때, package-info.java에 선언된 @ApplicationModule을 탐색한다.
하지만, Kotlin은 Java의 package-info.java 파일처럼 패키지 자체에 메타데이터를 정의하는 방식을 지원하지 않기 때문에, package-info.kt라는 파일을 만들어도 특별한 의미를 가지지 않고, 일반 Kotlin 파일과 다를 바가 없다.(즉, Spring Modulith가 모듈의 메타데이터로 인식하지 않는다.)
그래서 Spring Modulith에서는 Kotlin에서 패키지 메타데이터를 정의하기 위해 클래스 수준에서 선언하는 방식을 권장한다. (공식문서에서는 애플리케이션 모듈의 루트 패키지에 위치한 단일 클래스나 타입에 사용할 수 있다고 작성)
즉, moduleMetadata라는 단일 클래스를 생성하여 @ApplicationModule을 사용해야 한다.
해결
정리
공식문서 잘 읽자
The text was updated successfully, but these errors were encountered: