A Simple Suggestion to Radically Improve Your Package Structure
A burning issue.
Here's the thing.
In Java, all classes can see all the public classes in all other packages.
This blushing promiscuity generates astronomical quantities of potential coupling, the measure of the maximum number of dependencies that can be formed in a system. The more dependencies that can potentially form between packages, the greater the tendency that those dependencies will actually form, solidifying your package structure into a rigid creaking nightmare with ripple-effects up the wazoo.
Really, if your mattress is bulging with unwanted cash then paying programmers to maintain a pile of fabulously inter-dependent packages is a great way to get rid of it.
The solution's simple: restrict package access.
One way to achieve this is to allow packages to depend only on antecedents, that is, only on those packages that "come before" it in the package's fully-qualified name. This is radial encapsulation.
If you have package com.cheesepaint then it can depend only on the single package com.
If you have package com.cheesepaint.model then it can depend only on the two packages com.cheesepaint and com.
Any class in package com that depends on any class in either com.cheesepaint.model or com.cheesepaint is condemned to face the refactoring squad.
Objection, your honour!
You're thinking: hang on, how does this help?
This helps because it radically reduces the amount of dependencies that you can create in your system. At a stroke most of your packages will fall off one another's radars. With potential coupling slashed the tendency to form horribly-connected systems evaporates along with the ensuing costs.
Your honour, I really must object!
You're thinking: hang on, Java ain't got no such restriction.
No, it hasn't. This is an extra-linguistic rule. The compiler won't catch it. Enforce it by style guideline. Enforce it by code reviews. Write a script. Do it any way you like. The compiler won't help.
Your honour, this is an outrage!
You're thinking: hang on, what if I have com.cheesepaint.controller and it darn-tutin' has to depend on com.cheesepaint.model?
Two packages can only communicate via their (usually highest) common antecedent, which in this case is com.cheesepaint. So com.cheesepaint.model model must export a facade (family of classes or interfaces) into com.cheesepaint and com.cheesepaint.controller has to depend on that facade rather than on com.cheesepaint.model directly.
But, your ... !
You're thinking: hang on, Java only has one main() method: it can't be in both com.cheesepaint.controller and com.cheesepaint.model. So something must depend on both?
Yes of course this restriction is impossible to satisfy in Java but we won't let that stop us. Just make sure that whatever depends on both com.cheesepaint.controller and com.cheesepaint.model is utterly trivial: maybe only a Starter class that does nothing but tell the packages to store their facades in "lower" packages, ready for later use. The rule is relaxed only for such initialization maneuvers.
Slash package-access and thus slash potential coupling, frustration, development-time, hair-loss, weight-gain and costs.