Is package by feature approach good? [closed]
Recently I came across this javalobby post http://java.dzone.com/articles/how-changing-java-package on packaging java code by feature.
I like the idea, but i have few questions on this approach. I asked my question but didn't get a satisfactory reply. I hope someone on StackOverflow can clarify my questions.
I like the idea of package by feature which greately reduces the time for moving across the packages while coding and all the related stuff will be at one place(package). But what about interactions between the services in different packages?
Suppose we are building a blog app and we are putting all user related operations(controllers/services/repositories) in com.mycompany.myblog.users
package. And all blog post related operations(controllers/services/repositories) in com.mycompany.myblog.posts
package.
Now I want to show User Profile along with all the posts that he posted. Should I call myblog.posts.PostsService.getPostsByUser(userId)
from myblog.users.UserController.showUserProfile()
?
What about coupling between packages?
Also wherever I read about package by feature, everyone says its a good practice. Then why many book authors and even frameworks encourage to group by layers? Just curious to know :-)
Solution 1:
Take a look at uncle Bob's Package Design Principles. He explains reasons and motivations behind those principles, which I have elaborated on below:
Classes that get reused together should be packaged together so that the package can be treated as a sort of complete product available for you. And those which are reused together should be separated away from the ones those are not reused with. For example, your Logging utility classes are not necessarily used together with your file io classes. So package all logging them separately. But logging classes could be related to one another. So create a sort of complete product for logging, say, for the want of better name commons-logging package it in a (re)usable jar and another separate complete product for io utilities, again for the want of better name, say commons-io.jar. If you update say commons-io library to say support java nio, then you may not necessarily want to make any changes to the logging library. So separating them is better.
Now, let's say you wanted your logging utility classes to support structured logging for say some sort of log analysis by tools like splunk. Some clients of your logging utility may want to update to your newer version; some others may not. So when you release a new version, package all classes which are needed and reused together for migration. So some clients of your utility classes can safely delete your old commons-logging jar and move to commons-logging-new jar. Some other clients are still ok with older jar. However no clients are needed to have both these jars (new and old) just because you forced them to use some classes for older packaged jar.
Avoid cyclic dependencies. a depend on b; b on c; c on d; but d depends on a. The scenario is obviously deterring as it will be very difficult to define layers or modules, etc and you cannot vary them independly relative to each other.
Also, you could package your classes such that if a layer or module changes, other module or layers do not have to change necessarily. So, for example, if you decide to go from old MVC framework to a rest APIs upgrade, then only view and controller may need changes; your model does not.
Solution 2:
There many other aspect other than coupling for package design i would suggest to look at OOAD Priciples, especially package design priciples like
REP The Release Reuse Equivalency Principle The granule of reuse is the granule of release.
CCP The Common Closure Principle Classes that change together are packaged together.
CRP The Common Reuse Principle Classes that are used together are packaged together.
ADP The Acyclic Dependencies Principle The dependency graph of packages must have no cycles.
SDP The Stable Dependencies Principle Depend in the direction of stability.
SAP The Stable Abstractions Principle Abstractness increases with stability.
for more information you can read book "Agile Software Development, Principles, Patterns, and Practices"
Solution 3:
I personally like the "package by feature" approach, although you do need to apply quite a lot of judgement on where to draw the package boundaries. It's certainly a feasible and sensible approach in many circumstances.
You should probably achieve coupling between packages and modules using public interfaces - this keeps the coupling clean and manageable.
It's perfectly fine for the "blog posts" package to call into the "users" package as long as it uses well designed public interfaces to do so.
One big piece of advice though if you go down this approach: be very thoughtful about your dependencies and in particular avoid circular dependencies between packages. A good design should looks like a dependency tree - with the higher level areas of functionality depending on a set of common services which depend upon libraries of utility functions etc. To some extent, this will start to look like architectural "layers" with front-end packages calling into back-end services.