feat: Implement multi-registry support with separate feature modules#131
feat: Implement multi-registry support with separate feature modules#131finxo wants to merge 13 commits into
Conversation
… support multiple registries
…st module and documentation updates
adriangl
left a comment
There was a problem hiding this comment.
Thanks for the effort with the PR :). However, I have a very big concern I'd like to discuss: consumer coupling.
The current implementation is built under an assumption: the project's main module imports and configures Mini and modules provide extra stores or actions for the main Mini to link. While this approach may make sense in a multi-module project, if we bundle Mini inside a library, it would require that any module consuming it must:
- Be aware that Mini is being used in the library
- Know about and explicitly instantiate the library's
Storeclasses in the module - Pass those stores to
Mini.link()
This means that if a library uses Mini internally, its consumers are forced to depend on Mini too. That breaks a fundamental principle of library design: implementation details must stay internal. A consumer of a library should just call its public API, not know or care that it uses Mini under the hood.
We have to rethink this approach before we can merge this. The main blocker that currently disallows using it with multiple modules is that we currently create a unique Mini_Generated per project, no matter the modules or libraries that it contains, and that single instance is used by the Mini code to glue everything together. In the multi-module scenario, or lib + consumer that also uses Mini the build doesn't know which Mini_Generated prevails in compilation time and breaks. We should study how to create multiple instances so each module bundles its own Mini and we don't leak it so consumers don't need to implement it to use the module or lib.
One option that comes to my mind is to generate a MiniComponent or something like that that only belongs to the module where Mini is imported, and use that class to link everything inside the module. That way we wouldn't have any issues regarding multi-module conflicts and it would keep the Mini usage as implementation detail of the module or lib, so consumers don't need to implement Mini themselves. We may keep the concept of registries to do this too.
|
|
||
| junit = { group = "junit", name = "junit", version = "4.13.2" } | ||
| kluent = { group = "org.amshove.kluent", name = "kluent", version.ref = "kluent" } | ||
| timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } |
There was a problem hiding this comment.
No need to add Timber, we can use the standard Android Log class so we keep this unopinionated
| @@ -0,0 +1,130 @@ | |||
| /* | |||
| * Copyright 2024 HyperDevs | |||
There was a problem hiding this comment.
Please use the template in the .idea/copyright folder to generate this header, I doubt this code was written in 2024 😛.
Check the rest of the new files in the PR so they have the proper, updated copyright header
| private fun shortHash(value: String): String { | ||
| return value.encodeToByteArray().fold(0x811c9dc5.toInt()) { acc, byte -> | ||
| (acc xor byte.toInt()) * 16777619 | ||
| }.toUInt().toString(16) | ||
| } |
There was a problem hiding this comment.
Hmmmm... what is this? Can you explain it to me?
There was a problem hiding this comment.
It was a fallback to name every registry with a unique suffix, but now every Mini_Generated it's called like that and just change the package where it comes from
|
|
||
| dependencies { | ||
| implementation(project(":mini-common")) | ||
| kapt(project(":mini-processor")) |
There was a problem hiding this comment.
Use ksp for samples, kapt is in maintenance mode, basically: https://developer.android.com/build/migrate-to-ksp
| ## How to verify this change | ||
| The multi-registry implementation can be verified inside this repository without relying on an external host app: | ||
|
|
||
| ```bash | ||
| ./gradlew :mini-common:test | ||
| ./gradlew :mini-processor-test:test | ||
| ./gradlew :mini-processor-ksp-test:test | ||
| ./gradlew :mini-processor-reducer-only-test:test | ||
| ./gradlew :mini-processor-multiregistry-test:test | ||
| ``` | ||
|
|
||
| The `mini-processor-multiregistry-test` module is the smallest reviewer-facing example that demonstrates generated registries from different modules coexisting on the same classpath. | ||
|
|
||
| If you want to inspect the same idea in a running Android sample, launch the `:app` module and open `MultiRegistrySampleActivity`, which links two feature modules backed by separate generated registries. | ||
|
|
There was a problem hiding this comment.
README should not describe specific change verifications
| import timber.log.Timber | ||
| import java.io.Closeable | ||
|
|
||
| class MultiRegistrySampleActivity : AppCompatActivity() { |
There was a problem hiding this comment.
I think it would make more sense to bundle the multi-module setup as a separate project inside, let's say, a samples folder so we can test this use case in isolation. We can use composite builds there to import the needed Mini modules (or rely on Jitpack dependencies if we want to test with the real dependencies).
That way we can also remove some module overhead from the main Mini project.
There was a problem hiding this comment.
Examples has been divided in 2. One in a module within app, and other one outside in a samples' folder. That way I think every possibility of using mini is tested
| For a reviewer-facing multi-registry example, see the JVM integration test module `mini-processor-multiregistry-test`, which loads generated registries from both KAPT and KSP modules on the same classpath. | ||
| For a visual Android demonstration, run the sample app and open `MultiRegistrySampleActivity`. |
There was a problem hiding this comment.
The sentences here are kinda odd, I may rewrite them so they're not focused to a reviewer
…stead of suffixes
…into a separate consumer project
What I have implemented now is a module-local Mini runtime model:
|
PR's key points
MiniRegistryinterface and refactorsMiniclass to support multiple registries discoverysample-counter-featureandsample-message-feature) demonstrating isolated registriesMultiRegistrySampleActivityto showcase runtime linking of separate feature registriesHow to review this PR?
MiniRegistryinterface andMini.link()implementationMultiRegistrySampleActivityin the sample app to see the feature in action./gradlew :mini-processor-multiregistry-test:testRelated Issues (delete if this does not apply)
Definition of Done