How changing Java package names transformed my system architecture
Changing your perspective even a small amount can have profound effects on how you approach your system.
Let’s say you’re writing a web application in Java. In the system you deal with orders, customers and products. As a web application, your classes include staples like PersonController
, PersonRepository
, CustomerController
and OrderService
. How do you organize your classes into packages?
There are two fundamental ways to structure your packages. Either you can focus on the logical tiers, like com.brodwall.myapp.controllers
, com.brodwall.myapp.domain
or perhaps com.brodwall.myapp.services.customer
. Or you can focus on the domain contexts, like com.brodwall.myapp.customer
, com.brodwall.myapp.orders
and com.brodwall.myapp.products
. The first approach is by far the most prevalent. In my view, it’s also the least helpful.
Here are some ways your thinking changes if you structure your packages around domain concepts, rather than technological tiers:
First, and most fundamentally, your mental model will now be aligned with that of the users of your system. If you’re asked to implement a typical feature, it is now more likely to be focused around a strict subset of the packages of your system. For example, adding a new field to a form will at least affect the presentation logic, entity and persistence layer for the corresponding domain concept. If your packages are organized around tiers, this change will hit all over your system. In a word: A system organized around features, rather than technologies, have higher coherence. This technical term means that a large percentage of a the dependencies of a class are located close to that class.
Secondly, organizing around domain concepts will give you more options when your software grows. When a package contains tens of classes, you may want to split it up in several packages. The discussion can itself be enlightening. “Maybe we should separate out the customer address classes into a com.brodwall.myapp.customer.address
package. It seems to have a bit of a life on its own.” “Yeah, and maybe we can use the same classes for other places we need addresses, such as suppliers?” “Cool, so com.brodwall.myapp.address
, then?” Or maybe you decide that order status codes and payment status codes deserve to be in the “com.brodwall.myapp.order.codes
” package.
On the other hand, what options do you have for splitting up com.brodwall.myapp.controllers
? You could create subpackages for customer, orders and products, but these subpackages may only have one or possibly two classes each.
Finally, and perhaps most intriguingly, using domain concepts for packages allows you to vary the design according on a case by case basis. Maybe you really need a OrderService
which coordinates the payment and shipping of an order, while ProductController
only needs basic create-retrieve-update-delete functionality with a repository. A ProductService
would just get in the way. If ProductService
is missing from the com.brodwall.myapp.services
package, this may be confusing or at the very least give you a nagging feeling that something is wrong. On the other hand, if there’s no Controller in the com.brodwall.myapp.product
package, it doesn’t matter much.
Also, most systems have some good parts and some not-so-good parts. If your Services
package is not working for you, there’s not much you can do. But if the Products
package is rotten, you can throw it out and reimplement it without the whole system being thrown into a state of chaos.
By putting the classes needed to implement a feature together with each other and apart from the classes needed to implement other features, developers can be pragmatic and innovative when developing one feature without negatively affecting other features.
The flip side of this is that most developers are more comfortable with some technologies in the application and less comfortable with other technologies. Organizing around features instead of technologies force each developer to consider a larger set of technological challenges. Some programmers take this as a motivating challenge to learn, while others, it seems, would rather not have to learn something new.
If it were my money being spend to create features, I know what kind of developer I would want.
Trivial changes can have large effects. By organizing your software around features, you get a more coherent system that allows for growth. It may challenge your developers, but it drives down the number of hand-offs needed to implement a feature and it challenges the developers to improve the parts of the application they are working on.
See also my blog post on Architecture as tidying up.
Comments:
[Hildeberto Mendonça] - Jul 12, 2012
We actually need a mix of both, not one or another. Reshaping your examples:
com.brodwall.myapp.customer com.brodwall.myapp.customer.controller com.brodwall.myapp.customer.domain com.brodwall.myapp.customer.service
com.brodwall.myapp.orders com.brodwall.myapp.orders.controller
com.brodwall.myapp.orders.domain
com.brodwall.myapp.orders.service Â
com.brodwall.myapp.products com.brodwall.myapp.products.controller
com.brodwall.myapp.products.domain
com.brodwall.myapp.products.service
Johannes Brodwall - Jul 12, 2012
I disagree. Either your customer package isn’t properly thought through or your controller, domain and services packages will around one class each, making them meaningless.
Zdenek Farana - Jul 12, 2012
I like the approach, it appeals to me. What about cross-cut concerns? They happen.
Rune Flobakk - Jul 13, 2012
I agree wholeheartedly! As a bonus, structuring classes this way often enables to take advantage of package visibility, dividing behaviour into small focused and composable classes (instead of often misused inheritance), without exposing those classes outside the package.
Johannes Brodwall - Jul 13, 2012
I usually end up with a package like .myapp.common.persistence, for example. I try and keep the contents as small as possible and to avoid references from .common to domain specific packages.
Johannes Brodwall - Jul 13, 2012
Excellent point, Rune. I recently tried running Unnecessary Code Detector (an Eclipse plugin) on my project, and found that it pointed out many places where I could reduce visibility to package scope.
[rompetroll] - Jul 17, 2012
I see your point and can’t really disagree. One thing bothers me though…
What I like about “technological” packages is that I know where to start when I’m new to the application. If I see a ‘controller’ package, then I know that must be where the action mappings happen. So if I have a look into all the classes in this package, I will get an overview of all the actions that this app understands. When organizing by domain, I imagine I would have to grep and search to find e.g. all controllers. In other words, I wonder if it would be harder to maintain the project if you have not worked with it before.
PS: since this is about packages… why not drop the com.brodwall? Just myapp.orders, myapp.customer etc looks much nicer in a project tree. And if crediting the company is important, the class comments are also a good place for that.
Johannes Brodwall - Jul 17, 2012
I’m not sure about the starting time, but I don’t think it’s a significant factor either way. If I’m just given some code and asked to understand it,I usually start with the web.xml file. On the other hand, if I come into the application with a task in mind (change the way customers work), the domain oriented organization may be easier.
I agree about the package prefix. “com.brodwall” is just paranoid noise.
[Ketan] - Oct 22, 2012
I like your idea of representing the packages. I have few questions related to package naming: 1. How would you handle utility classes (DateUtil, StringUtil or some other utility related to your application?) 2. Considering the web application, where do you put support classes (like filters, displayTag decorators and so on)
Johannes Brodwall - Oct 24, 2012
Great questions, Ketan. But I suspect you already know the answers. I’ll answer them in reverse order.
For some support classes, they belong to a domain package. For example UserLoginFilter may belong to the …myapp.user package. A DisplayNameTag may belong to the person package.
When the support class is truly independent of the domain concepts (StringUtil, probably. DateUtil - maybe not - maybe this is the time package) I put them in a …myapp.common or …myapp.infrastructure. And I wear a rubber band around my wrist that I snap against my arm every time I do this.
[Robert] - Jul 26, 2013
I’m wondering if your thinking on this has changed at all in the last year. While I agree with your approach I have two areas I stumble on. One is for a multi-module approach and the other is for “swapping out” implementations. For example, moving from Spring to GWT. My bigger concern is the first.
Let’s say I have various services. Some of the code is for the client only (usually interfaces), some if for the server jar only, and then lets say I have another jar that is used by both. Would you consider the following?
com.app.user.core com.app.user.service // interfaces com.app.user.server // implementation
If so, then some of these packages my only have a single class or interface. Then if I have another concept such as “contacts” that contains a lot of special contact handling code, I end up with another package with three layers. Creating my services jar might have a bunch of packages with one interface in each (com.app.user.service, com.app.contacts.service, etc). This gets even more complicated because in my server jar will not list the common or interfaces packages kind of undoing the value of keeping them together.
On the second issue, it seems like a maintenance problem. If all my classes are in a domain package, how do I create replaceable parts? Say I want a GWT AND a Spring MVC implementation? The domain model (service layer) shouldn’t really care about that at all, right? So we end up with a separation of concerns issue.
Again, I like packages by domain much better than packages by layer. But I’m not sure how best to deal with these two issues.
[Robert DiFalco] - Jul 28, 2013
I also don’t use GWT, was just making an example. But I do use heroku for my REST server. I need the rest server to be stateless so that I can run any number of them. Same with the backend workers. Then I also need a scheduler process that there will only be one of. With one monolithic server process things are easier. :) It seems like your suggestion is to package by module and then domain/feature. Some of these may only have a single class, for example the REST service module would only have one UserResource whose methods would delegate to the worker module’s “user” package. The way I have it now is that the “server.rest” package has one JAX-RS class for each domain. There are about five of them and I put them all in the root of one package. Then in the server’s “Worker” module I have things more like you express - each feature has a package. Each feature package has a “shared” and a “server” sub-package. The “shared” interfaces go into a separate jar from “server” so that they can be used as the remote interface to the Worker in the REST servers. I don’t love it because me Shared module ends up with things like “user/shared”, “notifier/shared”, “contacts/shared”, “accounts/shared” and the same thing in the Worker module but with “server” instead of “shared” in the package name. This has its own problems and is in some ways more complex than “shared/user”, “shared/notifier”, “shared/contacts”, and “shared/accounts”. Especially if those domains have sub-packages. I’ve thought about this a lot and I can’t come up with a solution I love for a multi-module distributed server. For a single server module its much simpler – I just go with package by domain. Thanks for your feedback.
Johannes Brodwall - Aug 2, 2013
My first impulse would be use the same deployment artifact to all the REST servers and backend worker. The role of a server is then purely a matter of configuration.
There are several reasons teams feel this is uncomfortable. Almost all of these reasons also happen to be impulses that limit your agility.
Your description sounds like a fair solution if you’re afraid to take this step. ;-)
Johannes Brodwall - Jul 27, 2013
Hi Robert
Good questions. My basic thinking has, if anything been reinforced since I wrote the original post, but my setting is different from you.
First, even though I have worked in many projects where someone was concerned about the ability to reuse the domain logic in a different context, in all cases, this turned out to be an imaginary requirement (like many “architectural requirements”). My exposure to GWT also left me screaming away in the opposite direction.
The closest situation I’ve had to your concern was that of a client-server Swing application. In this case, we put all the Java code in a JAR that was reused by a (mostly empty) WAR file on the server side and a (completely empty) executable JAR on the client side. The potential savings from separating out server-only and client-only was ridicilously small as most of the distribution size was caused by the same dependencies (notably Spring) and not the application code.
In your case with GWT, I would first analyze whether the savings would be negligible. Using framework code in the application could negate this. If so, my first instinct would be com.app.core., com.app.server. and com.app.gwt. - each with a separate JAR. The Core, GWT and server components can be almost considered separate applications and may as well be treated as such. In most cases, the “domain model” (that is, entities) would reside in the Core, as would anything that wouldn’t cause trouble to deploy on GWT, such as Spring-independent services and possibly even controllers. The server-JAR contains the small pieces that are needed to make the Core interact with Spring. If the promise of Spring is taken to its fullest, this would only be configuration.