spring-modulith icon indicating copy to clipboard operation
spring-modulith copied to clipboard

Allow nested packages for @Modulith(sharedModules)

Open tvogel8570 opened this issue 11 months ago • 5 comments

Current Behavior The sharedModules parameter of the @Modulith annotation does not allow nested packages, even when the nested packages are explicitly listed. package structure - com.app.core.exception

@Modulith(sharedModules={"com.app.core"}, useFullyQualifiedModuleNames = true) and @Modulith(sharedModules={"com.app.core", "com.app.core.exception"}, useFullyQualifiedModuleNames = true) result in java.lang.IllegalArgumentException: Module com.app.core.exception does not exist!

@Modulith(sharedModules={"com.app.core"}, additionalPackages = { "com.app.core.exception"}, useFullyQualifiedModuleNames = true) results in Module 'com.app.user' depends on non-exposed type com.app.core.exception.EntityNotFoundException within module 'com.app.core'!

Desired Behavior Allow for wildcard(1) or explicitly listed(2) sub-packages to be referenced within all modules. 1 @Modulith(sharedModules={"com.app.core.*"}, useFullyQualifiedModuleNames = true) 2 @Modulith(sharedModules={"com.app.core", "com.app.core.exception"}, useFullyQualifiedModuleNames = true)

Workaround change package structure to com.app.core and com.app.exception with @Modulith(sharedModules={"com.app.core", "com.app.exception"}, useFullyQualifiedModuleNames = true)

tvogel8570 avatar Mar 07 '24 17:03 tvogel8570

There simply is no such thing as nested modules, yet. In other words, if a module named com.app.core exists, no module com.app.core.exception can exist at all. Note, that the attribute is sharedModules, not sharedPackages. That the module names look like packages is a side effect of the setting to enable qualified module names.

Can you take a step back and elaborate why you think you'd need to list that nested package in the first place? Shared modules are automatically included in integration test executions and effectively lead to the component scanning to include the package of the shared module. That means that declaring com.app.core a shared module, this will include – and thus bootstrap – code in nested packages such as the ….exception one anyway.

odrotbohm avatar Mar 08 '24 13:03 odrotbohm

Thanks for getting back to me so quickly! I am VERY new to modulith concepts so appreciate your input and guidance.

I have a set of common classes in 3 packages that will be used across all modules, e.g. exceptions., response. and util.*. The only way I could get the .verify() to work was to add a sharedModules annotation for each fully qualified package. Ideally, from my perspective, I would be able to have these packages in the same hierarchy (com.app.core) and just use it in the sharedModules annotation.

Is there another way to indicate that a class is shared across modules?


From: Oliver Drotbohm @.> Sent: Friday, March 8, 2024 8:17 AM To: spring-projects/spring-modulith @.> Cc: Timothy Vogel @.>; Author @.> Subject: Re: [spring-projects/spring-modulith] Allow nested packages for @Modulith(sharedModules) (Issue #524)

There simply is no such thing as nested modules, yet. In other words, if a module named com.app.core exists, no module com.app.core.exception can exist at all. Note, that the attribute is sharedModules, not sharedPackages. That the module names look like packages is a side effect of the setting to enable qualified module names.

Can you take a step back and elaborate why you think you'd need to list that nested package in the first place?

— Reply to this email directly, view it on GitHubhttps://github.com/spring-projects/spring-modulith/issues/524#issuecomment-1985680852, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ACBEBVWIBDWGL7EICZKX5ZLYXG277AVCNFSM6AAAAABELN2L4SVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSOBVGY4DAOBVGI. You are receiving this because you authored the thread.

tvogel8570 avatar Mar 09 '24 11:03 tvogel8570

No worries. I am a bit surprised you state that you could get this to work, as the shared modules definition is not even considered during the verification. The Javadoc of the attribute clearly states that it's used to define modules that are supposed to be included in integration test executions (using @ApplicationModuleTest) but it will not change anything about the outcome of a AppplicationModules.of(…).verify().

As documented here, nested packages of application module base packages are considered internal and other modules are not allowed to refer to code within those by default. I.e., it is not surprising that references to those would break the verification. To expose such types to other packages, the package they reside in needs to be declared a named interface.

We generally discourage the use of packages to group types by their stereotype (exceptions, controllers, services etc. getting their own package) as that fundamentally subverts the encapsulation effects of packages. That said, Spring Modulith 1.2 will ship with the notion of an “open module”, which would also expose its externals (in other words: all nested packages) to other modules.

If you find the time, it would be helpful to look at a reproducer as that allows us to find out what we're really talking about here.

odrotbohm avatar Mar 09 '24 14:03 odrotbohm

  1. As you indicated, sharedModules did in fact NOT enable me to use nested packages. The test was green b/c I made them top level packages therefore modules (duh on my part).
  2. After a bit of time to RTFM, what I need is @NamedInterface to make my original package layout work.
  3. WRT grouping types by stereotype, I have top-level packages, i.e. modules, named with Domain model names. Within those top-level packages, I was grouping by stereotype. Is that also discouraged? If so what sub-package approach do you recommend?

From: Oliver Drotbohm @.> Sent: Saturday, March 9, 2024 9:25 AM To: spring-projects/spring-modulith @.> Cc: Timothy Vogel @.>; Author @.> Subject: Re: [spring-projects/spring-modulith] Allow nested packages for @Modulith(sharedModules) (Issue #524)

No worries. I am a bit surprised you state that you could get this to work, as the shared modules definition is not even considered during the verification. The Javadoc of the attribute clearly states that it's used to define modules that are supposed to be included in integration test executions (using @ApplicationModuleTest) but it will not change anything about the outcome of a AppplicationModules.of(…).verify().

As documented herehttps://docs.spring.io/spring-modulith/reference/fundamentals.html#modules.advanced, nested packages of application module base packages are considered internal and other modules are not allowed to refer to code within those by default. I.e., it is not surprising that references to those would break the verification. To expose such types to other packages, the package they reside in needs to be declared a named interfacehttps://docs.spring.io/spring-modulith/reference/fundamentals.html#modules.named-interfaces.

We generally discourage the use of packages to group types by their stereotype (exceptions, controllers, services etc. getting their own package) as that fundamentally subverts the encapsulation effects of packages. That said, Spring Modulith 1.2 will ship with the notion of an “open module”, which would also expose its externals (in other words: all nested packages) to other modules.

If you find the time, it would be helpful to look at a reproducer as that allows us to find out what we're really talking about here.

— Reply to this email directly, view it on GitHubhttps://github.com/spring-projects/spring-modulith/issues/524#issuecomment-1986870800, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ACBEBVUJKABGNIQLUJOM5D3YXMLVRAVCNFSM6AAAAABELN2L4SVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSOBWHA3TAOBQGA. You are receiving this because you authored the thread.

tvogel8570 avatar Mar 09 '24 16:03 tvogel8570

Hello @odrotbohm, Firs thanks for your work on Spring, it's always a great pleasure to work with it. We started using Modulith on a project and it allows us to detect invalid dependencies. But the nested item is not clear for me. Regarding your comment "To expose such types to other packages, the package they reside in needs to be declared a named interface."

You reference this:

The effect of that declaration is twofold: first, code in other application modules is allowed to refer to SomeSpiInterface. Application modules are able to refer to the named interface in explicit dependency declarations. Assume the inventory module was making use of that, it could refer to the above declared named interface like this: ..

I have 2 questions:

  • If you have nested packages, is this expected to work foo::nestedlevel1::nestedlevel2 ? I understand it's not recommended. Maybe the last point should be added or highlighted to documentation, I may have missed it.
  • Regarding "For modules without explicitly described dependencies, both the application module root package and the SPI one are accessible.", it's not clear, do it mean that if another module (not nested) does not declare any AllowedDependencies it would be the foo and all NamedInterface in it ?

Thank you, hoping my questions are not too stupid.

pmouawad avatar Mar 21 '24 15:03 pmouawad