Packaging by feature for loose coupling

Transforming Coupang’s legacy architecture for Android — part 3

Coupang Engineering
Coupang Engineering Blog
10 min readDec 31, 2018

--

By Seongchul Park

Series index

This is part 3 of a series on “Transforming Coupang’s legacy architecture for Android.”

This post is also available in Korean.

Packaging strategies were often overlooked by early mobile apps, and it was common to place multiple classes in a single package. As the app grew, they had to create packages from components like activities or fragments and reorganize the classes. This kind of development results in having multiple classes in a package and becomes a major obstacle for maintaining the app.

Despite successfully splitting the codebase into 13 core components, our feature module also contained packages that were divided by components. We had a very complex packaging dependency and needed to repackage the app. In this post, we will look into how we removed the package dependencies for a more continuous modularization.

Packaging by components vs. features

We compared the two ways to structure our project: structuring the package in components like the activity, fragment, adapter, and handler, or structuring the packaging in features where a single feature can contain an activity, fragment, or adapter. We came to the conclusion that packaging by features was a better option for us.

Scalability: The main advantage of packaging by features is scalability. We can easily create a new package for a feature or reorganize a feature that has expanded over time into smaller set of features. If we package by components like the activity, we may waste a lot of time looking for the relevant class.

Removability & Readability: Developers put their best efforts to deliver a feature that is useful. However, in practice, some features are removed after being released to the users. By packaging by features, we can easily remove them from the top-level feature package without having to remove it from each component. Also, the focus on the feature enables a developer to easily understand the intent of the feature.

Dependencies: Packaging by features will mean that most dependencies will be internal and only the common codes will have dependencies outside of the packages. We can also use accessors to reduce dependencies further by restricting access to certain classes. Moreover, we can split down a feature-based package into components or layers for efficiency, readability, and maintainability. For example, if were to create a feature-based package in the MVP pattern, we can create components of view, model, and presenter as a sub-package.

Repackaging process

To minimize duplicate codes and dependency, we placed the classes of the common features in a package named ‘common’ package in the top-level package like below.

For the classes that are only used internally for specific features, we placed them in a package by feature like below.

We also created a package called ‘domain’ in the top-level package, and assigned a domain to a single domain package. Until recently at Coupang, each of the engineering team were responsible for a business domain, like search, login, and cart, and all its respective features. For example, if a team was responsible for a ‘search’ domain, it would be responsible for all its features like home search, map search, and auto-complete. So, we were lucky enough to easily distribute the work of repackaging by making use of the team organization.

Here are some tips we would like to share for those preparing to repackage their project.

  • Use the built-in refactoring tools of Android Studio. For repackaging, we must carefully check the files or class paths declared in the XML files like the manifest or layout. Android Studio provides some useful features to update it automatically.
  • Use marker interfaces to update ProGuard configurations. To avoid setting up the ProGuard configurations again after each package update, use the marker interface on all classes. For example, we created a DTO interface on all DTOs so that the ProGuard configurations were applied to all classes implementing the interface.
  • Use the visualization or analysis tools to check your repacking efforts. Code Iris is an Android Studio plugin and shows the dependencies of modules, packages, and classes of your project. APK Dependency Graph shows the coupling status of classes. MetricsReloaded shows the analysis of packages and classes with dependencies.
Image showing visual representation of the Coupang app’s complex dependencies before repackaging
Figure 1. Coupang app’s dependencies before repackaging, visualized by the APK Dependency Graph
Image showing visual representation of the Coupang app’s with less dependencies after repackaging
Figure 2. Coupang app’s dependencies after repackaging, visualized by the APK Dependency Graph

Conclusion

Of course, some level of dependencies is unavoidable but as shown in Figure 1, our efforts of repackaging the app made a difference. We achieved scalability, removability, readability as well as easy reusability of common classes over various domains. We are still in progress of modularization, but we are no longer a big ball of mud!

We’re actively looking for passionate individuals who are not afraid to ask questions, challenge the norm, and make things happen. Check out our open positions or sign up for job alerts!

--

--

Coupang Engineering
Coupang Engineering Blog

We write about how our engineers build Coupang’s e-commerce, food delivery, streaming services and beyond.