Hello team So I've been thinking of the recommenda...
# android-architecture
r
Hello team So I've been thinking of the recommendation on modularization while using Nav3 from the Google team. https://developer.android.com/guide/navigation/navigation-3/modularize?authuser=1#separate-features Previously, on our set up with nav2, we bubbled up events to say the root to handle navigation events. Feature modules did not know about the existence of other feature modules. But the new recommendation suggests creating
impl
and
api
submodules and have the feature modules depend on the needed
api
module. Something in me just doesn't sit right with that recommendation. What are you thoughts and how are you approaching this.
e
Keeping in mind that nav3 uses navigation keys to navigate eg.
Copy code
data object LocationsListScreen

data class LocationDetailScreen(val id: LocationId)
Anyone that needs to open a screen needs to do so by specifying this key. For them to have access to the key they need to have access to those classes. Now, there are two ways: either put all keys in a singular common module that rest of the app has access to OR each small unit of the app exposes their key in some way. If you do the former you will very soon (in a medium to large app) run into issues with scaling such an approach. The latter is what is recommended in the docs. Now, another way to target screens, that could stand as a substitute for keys with support for parameters is deep links. So if exposing keys in public api is not desirable for your project, just support a mapping from deep link to key and use deep links instead
👍 1
b
Putting the keys into your api module shouldn't be a problem. At Square we did that all the time just instead of a nav key it was a workflow. Parent modules only see the key and not the impl so there's never a dependency cycle. See this for how it should operate. https://amzn.github.io/app-platform/module-structure/#module-rules The arch is slightly different but it's the same principle.
👍 1
r
Something in me just doesn't sit right with that recommendation.
What in particular does not "sit right" with you? Is it simply that it's a new and unfamiliar concept? @fal was wrestling with the same concept in https://kotlinlang.slack.com/archives/CJLTWPH7S/p1778668709954829. He got some good feedback from the community. In my experience, the
api
/
impl
module pattern is by far the most maintainable approach. It scales well as the app and team maintaining it grows. I've also followed the approach you mentioned—bubbling events up to a central module—and that's never scaled well. I've employed the
api
/
impl
approach on multiple apps that I've worked on as of late with good success.
♥️ 1
r
Thanks all for your comments. After reading through the reasonings I understand a bit more why that approach is favoured
u
Btw isnt there noticable overhead to doubling all modules with such api module?
b
Yes and no. For KMP if you have iOS targets, syncing can be slow since you have more modules to sync. However one of the real benefits is that if you make a change in one module among many then the compilation times are significantly faster. Also it allows you to make demo/fake/testing modules which can help you make really robust test systems. Plus the api modules are typically small and just contains interfaces or data classes. There’s not a lot going on in them usually so there’s not a lot to sync. In my experience they have been very lightweight
u
yea i already use the pattern, just not for ui, before we settled on a single catch all module, but i never reslly like it collected various listeners and event types etc, basically union of all public apis technically im all for it im just not sure about the config times it will bring so these can be just jvm, i.e no android, right?
b
Yeah some of them can just be jvm. I believe we had that at Square and it saved some time syncing (which was always kind of a nightmare with 4500+ modules) You just have to be diligent about marking them. But that should be easier with gradle plugins. This is all if my memory serves me correctly. I’m 90% confident in my answer 😅
u
marking them why how? do you mean as jvm as opposed to android?
b
As in instead of applying a plugin that contains
com.android.library
for instance, it would just be
kotlin("jvm")
u
yea… i mean isolated project cant come soon enough, changing arch due to build times is lame
@blakelee if I may, at your scale, in other companies one would expect bazel, no? wasn’t that a consideration?
b
I think Meta does that. Square stuck with gradle but they did have some serious developer experience issues and workarounds they had to do to support all of those modules — such as scripts to only sync a subset of the total amount per vertical.
u
hm bazel doesn’t require sync? i though that was mostly an ide thing (aligning ide with build system)
b
Syncing in the repo, yeah. Which now in the age of AI maybe it’s not a problem as much. I haven’t used bazel so I can’t comment on it
u
i dont follow, what sync do you mean? AS sync?
b
Yes, Android Studio
u
i would still expect there to be ide sync with bazel, i mean even maven has it.. its probably Just fast so nobody cares?
btw dod you guys have some sort of script or ide integration to generate the api+impl module pair?
b
Yeah, there was custom tool windows built that would allow you to select which module types you wanted to add. At this point it might be easy enough to just ask an agent to make it for you. We’re getting pretty off topic now so if you want more info feel free to DM me
😅 1
r
I'm pretty sure I wasn't the only ne enjoying the off topic conversations @blakelee
m
I have been using a more thorough decomposition lately, specifically, each feature has its module set like
{contract, domain, data, impl, ui}
, with rules for interdependencies provisioned via
konsist
,
{contract, ui}
are public api surface, impl is only depended on by aggregate app nodes,
{domain, data}
are internal to the feature Ui specific and local to the feature goes into
impl
along with state holders and bindings while reusable ui components specific to the feature aka not design system elements go into
ui
, so far this has been working great allowing for unification of the overall code base on several layers
♥️ 1