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:
tld.domain.project.model.Company tld.domain.project.model.User tld.domain.project.controllers.CompanyController tld.domain.project.controllers.UserController tld.domain.project.storage.CompanyRepository tld.domain.project.storage.UserRepository tld.domain.project.service.CompanyService tld.domain.project.service.UserService
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:
tld.domain.project.company.Company tld.domain.project.company.CompanyController tld.domain.project.company.CompanyService tld.domain.project.company.CompanyRepository tld.domain.project.user.User tld.domain.project.user.UserController tld.domain.project.user.UserService tld.domain.project.user.UserRepository
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 tld.domain.project.support
,
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.