Package-by-Feature with the Default Access Modifier.

A lot of business applications are written using a three-tier architecture: View, Business, and Data layers, and all model objects are used by all three layers. In some codebases, the classes for these applications are organized by layer. In some application, which have the need to register various users and the company they work for, the code structure would result in something like:


Using such a package-by-layer structure for your classes requires a lot of methods to be public. The UserService needs to be able to read and write Users into storage and, since the UserRepository is in another package, almost all methods of the UserRepository would need to be public.

The organization might have a policy to send an email to a user, to notify them when their password has been changed. Such a policy might be implemented in the UserService. Since the methods in the UserRepository are public, there is no protection against another part of the application invoking a method in UserRepository, which changes the password but does not trigger the notification to be sent.

When this application is updated to include some customer-care module or a web-care interface, some of the features in those modules might want to reset the password. Since these features are built at a later point in time, perhaps after new developers have joined the team, these developers might be tempted to access the UserRepository directly from a CustomerCareService instead of calling the UserService and triggering the notification.

The Java language provides a mechanism to prevent this: access modifiers.

The default access modifier means we do not explicitly declare an access modifier for a class, field, method, etc. A variable or method declared without any access control modifier is available only to other classes in the same package. This is also called package-private.

In order to benefit from that access protection mechanism, the code base should be organized into a package-by-feature package hierarchy. The same classes as before would be packaged like this:

When organized like this, none of the methods in the UserRepository would have to be public. They all could be package-private and still be available to the UserService. The methods of the UserService could be made public.

Any developer building the CustomerCareService, in the package, would not be able to invoke methods on the UserRepository and should call the methods of the UserService. This way the code structure and the access modifiers help to ensure that the application still adheres to the policy to send the notification.

This strategy for organizing the classes in your codebase will help reduce the coupling in your codebase.

This article was written by me as a contribution for the book 97 Things Every Java Programmer Should Know. Many thanks to Trisha Gee for her effort in reviewing it.

Cover of the book '97 Things Every Java Programmer Should Know'