In package-by-feature, the package names correspond to important, high-level aspects of the problem domain. For example, a drug prescription application might have these packages:
com.app.doctor
com.app.drug
com.app.patient
com.app.presription
com.app.report
com.app.security
com.app.webmaster
com.app.util
com.app.doctor
package might contain these items:
DoctorAction.java
- an action or controller object
Doctor.java
- a Model Object
DoctorDAO.java
- Data Access Object
It's important to note that a package can contain not just Java code, but other files as well. Indeed, in order for package-by-feature to really work as desired, all items related to a given feature - from user interface, to Java code, to database items - must be placed in a single directory dedicated to that feature (and only that feature).
In some cases, a feature/package will not be used by any other feature in the application. If that's the case, it may be removed simply by deleting the directory. If it is indeed used by some other feature, then its removal will not be as simple as a single delete operation.
That is, the package-by-feature idea does not imply that one package can never use items belonging to other packages. Rather, package-by-feature aggressively prefers package-private as the default scope, and only increases the scope of an item to public only when needed.
com.app.action
com.app.model
com.app.dao
com.app.util
Higher Modularity
As mentioned above, only package-by-feature has packages with high cohesion, high modularity, and low coupling between packages.
Easier Code Navigation
Maintenance programmers need to do a lot less searching for items,
since all items needed for a given task are usually in the same directory.
Some tools that encourage package-by-layer use package naming conventions to ease the problem of tedious code navigation.
However, package-by-feature transcends the need for such conventions in the first place, by greatly reducing the need to navigate between directories.
Higher Level of Abstraction
Staying at a high level of abstraction is one of programming's guiding
principles of lasting value. It makes it easier to think about a problem,
and emphasizes fundamental services over implementation details. As a direct
benefit of being at a high level of abstraction, the application becomes
more self-documenting: the overall size of the application is communicated
by the number of packages, and the basic features are communicated by the
package names. The fundamental flaw with package-by-layer style,
on the other hand, is that it puts implementation details ahead of high level abstractions - which is backwards.
Separates Both Features and Layers
The package-by-feature style still honors the idea of separating layers,
but that separation is implemented using separate classes. The package-by-layer
style, on the other hand, implements that separation using both
separate classes and separate packages, which doesn't seem necessary
or desirable.
Minimizes Scope
Minimizing scope is another guiding principle of lasting value. Here,
package-by-feature allows some classes to decrease their scope from public to
package-private. This is a significant change, and will help to
minimize ripple effects.
The package-by-layer style, on the other hand, effectively abandons package-private scope,
and forces you to implement nearly all items as public
.
This is a fundamental flaw, since it doesn't allow you
to minimize ripple effects by keeping secrets.
Better Growth Style
In the package-by-feature style, the number of classes within each package remains
limited to the items related to a specific feature. If a package becomes
too large, it may be refactored in a natural way into two or more packages.
The package-by-layer style, on the other hand, is monolithic. As an application grows in
size, the number of packages remains roughly the same, while the number
of classes in each package will increase without bound.
If you still need further convincing, consider the following.
Directory Structure Is Fundamental To Your Code
"As any designer will tell you, it is the first steps in a design process which count for most. The first few strokes, which create the form, carry within them the destiny of the rest." - Christopher Alexander
(Christopher Alexander is an architect. Without having worked as programmer, he has influenced many people who think a lot about programming. His early book A Pattern Language was the original inspiration for the Design Patterns movement. He has thought long and hard about how to build beautiful things, and these reflections seem to largely apply to software construction as well.)
In a CBC radio interview, Alexander recounted the following story (paraphrased here): "I was working with one of my students. He was having a very difficult time building something. He just didn't know how to proceed at all. So I sat with him, and I said this: Listen, start out by figuring out what the most important thing is. Get that straight first. Get that straight in your mind. Take your time. Don't be too hasty. Think about it for a while. When you feel that you have found it, when there is no doubt in your mind that it is indeed the most important thing, then go ahead and make that most important thing. When you have made that most important thing, ask yourself if you can make it more beautiful. Cut the bullshit, just get it straight in your head, if you can make it better or not. When that's done, and you feel you cannot make it any better, then find the next most important thing."
What are the first strokes in an application, which create its overall form? It is the directory structure. The directory structure is the very first thing encountered by a programmer when browsing source code. Everything flows from it. Everything depends on it. It is clearly one of the most important aspects of your source code.
Consider the different reactions of a programmer when encountering different directory structures. For the package-by-feature style, the thoughts of the application programmer might be like this:
Package-By-Layer in Other Domains is Ineffective
By analogy, one can see that the package-by-layer style leads to poor results. For example, imagine a car. At the highest level, a car's 'implementation' is divided this way (package-by-feature) :
Now imagine a car whose 'implementation' under the hood is first divided up according to these lower level categories (package-by-layer) :
As a second example, consider a large bureacracy divided up into various departments (package-by-feature):
So why should software be any different? It seems that package-by-layer is just a bad habit waiting to be broken.
The example applications that come with WEB4J uses the package-by-feature style.