In the DZone comments under my post about Layered Architecture, some people believed that layering should be realized as what I call Packaging by Feature. I intentionally delayed writing about this to cover hexagons and onions first but now that we have those behind us, we can jump straight into the so demanded architectural style.
What Is Package by Feature?
In a typical Layered Architecture, code organization follows the horizontal decomposition created by dividing the system into layers. This is what is often called Package by Layer.
The problem with this approach is that cohesion inside each package is usually low and the coupling between packages is very high. This seems like exactly the opposite of what we want to achieve. At the same time, most people agree that separating the concerns of presentation, application, domain, and infrastructure makes sense. Package by Feature capitalizes on both points by moving the layering a level down — to the class level — and focusing on coupling and cohesion at a higher level by keeping all classes related to the same feature in the same package/module:
You might wonder now what is a feature? It’s a valid question. I have seen different implementations and examples, varying from a feature meaning effectively one use case to feature meaning a whole set of operations related to a particular business concern. I have seen more of the latter when doing research for this article, but I don’t think it’s necessarily a good thing.
The Essence of Package by Feature
I see two things that form the essence of Package by Feature: maintaining high cohesion and low coupling at the package/module level and being able to say what an application does by looking at its package/module structure.
In a typical Layered Architecture, the cohesion inside a package is low and the coupling between the packages is high. We rarely change technologies or conventions regarding a particular layer, comparing to how often we add a single feature, hence low cohesion. And, in most cases, each class in a layer (package) depends on at least one class in another layer (package), making the coupling between them high. In Package by Feature style, we ideally change classes only in a single package – the one related to the feature we’re working on – so cohesion is high, and there are only a few dependencies between features, so coupling is low.
The two terms above are useful but, to be honest, these are nerd metrics. The more human side of Package by Feature is that you don’t have to be a nerd that knows every piece of a codebase to be able to say what it does – a quick look at the package/module structure should tell you that. This is one step towards what Uncle Bob calls Screaming Architecture.
package com.ecommerce.catalog; // enables user to browse the products package com.ecommerce.checkout; // enables user to make an order package com.ecommerce.orders; // enables the shop to process the orders
Implementing Package by Feature
The name Package by Feature strongly suggests that you should implement this style by putting each feature in a separate package and that’s mostly true. The only caveat that I see is that it could also be a module, namespace or any other mechanism available in your language in case you’re not using Java.
The second important thing about Package by Feature is that when related stuff is in a single package, one can finally make some use of access modifiers i.e. make most of the things package-local instead of public. This way we can limit the public interface of our feature to just the things that we actually want to expose. In the Package by Layer style, this was not possible because related classes needed to communicate across packages, which forces us to make everything public.
Even though I feel like Spring Pet Clinic is not the best resource to learn about software development from, it does a pretty good job at presenting a flavor of Package by Feature. The features, in this case, are sets of operations performed on same domain objects:
As you can see, we have three dominating features here – managing owners, vets and visits. Whether this decomposition into features is good or not, is a very complicated topic. At the moment, we should focus on the fact that classes representing different layers like controllers, repositories, and domain objects are all kept together, and that the package structure is more feature-oriented.
Benefits of Package by Feature
- More Informative Package Structure – you can deduce some of the system’s main features or behaviors from the code structure
- High Cohesion & Low Coupling – we explained this point already in the essence part, although a bit idealistically
- Better Encapsulation – you can make effective use of access modifiers to hide the information that’s ought to stay hidden
- Better Growth Potential, in the code volume sense – unless you develop too big or too small features, you shouldn’t encounter the problem of too big or too many packages
Drawbacks of Package by Feature
- Unspecified Feature Size – I found no clear guidance or proof that one understanding of feature is better than the other
- Learning Curve – the concept is simple, but since you need to find your own way to slice a system into features, mastering it can be really hard
- Messiness Potential – in a tangled domain, in which everything is connected to everything, the slicing into features might become artificial and the low coupling promise might not be fulfilled
When to Use Package by Feature?
Obviously, it can work for smaller applications in which you can shuffle stuff around and try different things at a very low cost. At the same time, since it carries the promise of higher cohesion, lower coupling, and better growth potential, it seems like a perfect fit for bigger applications, which could actually suffer from the drawbacks of packaging by layer. Given its moderate learning curve i.e. the necessity to sort out the best definition of a feature, I’d be wary of using it for the first time when working on a critical application. I’d opt for dividing the problem into bounded contexts and implementing each one in a layered/hexagonal way instead.
Packaging by Feature is a really appealing idea and I’m totally not surprised that a lot of people push it forward. Our favorite nerdy metrics like cohesion and coupling seem good, and we can see what the application is doing by simply looking at packages. At the same time, I can see its drawbacks and the potential to become a huge mess if someone tries to use an artificial slicing into features in a tangled domain. If you know how to leverage its benefits effectively, go for it. If you don’t, be careful.