Summary
Layering is a powerful architectural pattern. It can be more powerful when using your deployable units as layer boundaries.

Advertisement

On a recent Fowler blog, he introduces a vote that occurred on some layering principles that he and a group of others identified while attending a conference. As he mentions, the list is hardly definitive, but interesting nonetheless given the convincing number of votes received by some of the principles. Here's a partial list of those principles with a difference greater than five on either the positive or negative side (the voting style used was to give everyone 10 positive/10 negative votes):

Most of these don't necessarily surprise me, and the majority of them are simply good practice that most everyone is already aware of.

There were a few interesting votes, however. I think they hit the nail square in recognizing that separating development teams by layer doesn't work as well as when separating by use case or functional area. And I've never actually heard of anyone distributing at layer boundaries, but I don't suspect it would work to well.

However, the one vote that surprised me most was the number of negative votes received (along with zero positive votes) by Layers should have separate deployment units. If we really feel so strongly about low coupling and high cohesion, a strong separation of concerns, no circular references, and independently testable layers, then isn't separating your layers into independently deployable units a great way to approach, and help enforce, each of these?

I don't think it's necessarily important that each layer be a single deployable unit, but instead that multiple layers not reside in a single deployable unit. By not separating your layers into deployable units, can you be certain your layers are independently testable? Can you guarantee that there are no cyclic references? Certainly tools such as JDepend are available to help, but separating layers into deployable units is equally, if not more powerful, and opens up other windows of opportunity as well.

While that's sounds interesting in theory it could be not so useful in practice. Requirements for deployment may be (and usually are) quite different than those for design/application logical architecture. For example, it's often a good choice to adhere to a single-file deployment model.

>>Requirements for deployment may be (and usually are) >>quite different than those for design/application logical >>architecture. For example, it's often a good choice to >>adhere to a single-file deployment model.

I actually find the exact opposite in practice. Most of the applications I've worked on end up separating out and deploying multiple modules. Most of these applications are Java webapps, and the modules are .jar files. I can't imagine deploying all classes in a single deployable unit, especially for large systems with thousands of classes. Do you care to comment further on why it's often a good choice to adhere to a single-file deployment model?

> I actually find the exact opposite in practice. Most of> the applications I've worked on end up separating out and> deploying multiple modules. Most of these applications are> Java webapps, and the modules are .jar files.

I didn't have a lot of Java-specific experience but see below.

> I can't> imagine deploying all classes in a single deployable unit,> especially for large systems with thousands of classes. Do> you care to comment further on why it's often a good> choice to adhere to a single-file deployment model?

Simplicity, for one. For example .war format allows you to bundle code and data into a single unit which you just drop at the right place.

I didn't have a lot of experience with developing large Java applications but I have used some big ones. And I did't remember I saw an app deployed as db.jar + ui.jar + bo.jar or something like that (separated by layers).

In my experience, separation based on a functinality or subsystems is much more widespread and a typical subsystem is more like a piece of a pie, which contains most if not all logical layers.

I think the wording is unfortunate, but the idea behind it is quite sound. Layer boundaries are excellent places to put more comprehensive tests in place, so if you can easily configure your application on layer boundaries, with test harnesses, you've got a leg up on the testing issue.

I personally concur with the votes from the workshop. The number of deployable units should be minimized and you need very good reasons to create separate units. I can actually think of two such reasons:

1. The deployment schedule of units is very different and needs to be independent. You don't want to tie the deployment of one unit that may be updated every couple of months with a unit that is updated every year. I cannot think of a layering example but I remember a discussion on comp.lang.python mentioning this as a reason that wxPython (a GUI library that is built around wxWidgets) is not integrated into the standard Python distribution.

2. A unit is reusable in different system deployments. I can't think right now of a layering example or any other example that fits this case.

When these reasons do not apply, a system should be broken into multiple deployment units only if its size is a problem and units are optional. For instance, RPMs in Linux. Well, you may also consider this as a third reason in the above list.

The biggest examples of layering that come to my mind are Unix piping of commands and Java streams. Neither one of them deploys in separate units.

So, let me pose this question. When you build your binary executables, typically the next step in the process is bundling those units and then testing them. During the testing process you should be able to ensure that you have clean seperation of your layers, whatever they may be. If you bundle your deployable binary as a single unit, i.e one jar file, you then lose any capability of being able to ensure that your dependencies are not invalid and not cyclic.

But if you are able to bundle into smaller logically, and possibly physically, related units, then you do have the capability to enforce those dependency restrictions. First by the compile time dependencies and second at runtime during your unit testing.

So, I guess my question is... how to you enforce those dependency restrictions without creating seperate deployment units? I guess there are always JDepend type reports, but that takes someone actually looking at them and determining if violations have occured.

My experience with Java is rather limited, but I would suggest that you can ensure the proper decoupling of the layers by running tests before bundling the units into the jar file, even if you have to run the same tests again on the jar file after bundling.

Anyway, IMHO, testing the proper decoupling of the layers is commendable but it should be only the development team's burden. The burden should not be passed on to the support team that installs the product and to customers of the product who will have more difficulty managing a complex deployment.

>>Simplicity, for one. For example .war format allows you to >>bundle code and data into a single unit which you just >>drop at the right place.

You can put your .jar files into the .war in the WEB-INF/lib directory. You deploy the same.

>>In my experience, separation based on a functinality or >>subsystems is much more widespread and a typical subsystem >>is more like a piece of a pie, which contains most if not >>all logical layers.

Sure. I also agree that a system should be separated based on functional areas and subsystems. But if you've got a functional component that includes all layers, that component is inevitably tied to the system using it. I don't eat pie much, but if I could buy a slice of pizza without the crust, I would.

>> The number of deployable units should be minimized and you >>need very good reasons to create separate unitsTestability? Maintainability? Reusability? Modularity? Separating your layers into deployable units helps with each.

>>2. A unit is reusable in different system deployments. I >>can't think right now of a layering example or any other >>example that fits this case.

How about a component that needs to be used in a web application and also in a batch process. You don't want all that web ui stuff deployed in batch. And recently, I've been developing a larger number of Java batch applications.

>>> The number of deployable units should be minimized and you >>>need very good reasons to create separate units> Testability? Maintainability? Reusability? Modularity?> Separating your layers into deployable units helps with> each.

I've already addressed testability in another comment. I don't see maintainability and modularity relevant at deployment level because developers understand and modify the code at code file level, not at jar file level. Reusability is okay and I mentioned it as reason #2.

>>I don't see maintainability and modularity relevant at >>deployment level because developers understand and modify >>the code at code file level, not at jar file level.

Developers certainly make the changes at the coding level, but they don't always understand the ramifications of change. With deployable units, you do understand the ramifacations. The units have relationships between each other, and we understand these relationships. The units are compiled with only what they need in their compilation classpath, not what the entire application needs. If you understand this, you have a fuller understanding of the system as a whole. The application is more modular and maintainable.

To rant a bit more about this...We separate code at the class level so classes are cohesive. We've always done this, and it's what most people think of when they speak OOAD. Developers are starting to spend more time designing package relationships, and realize that these relationships should by acyclic, that the classes within a package should be impacted by the same kinds of change, that we should separate interfaces/abstractions from the classes that use them, and more. Why do we do this if developers only need to understand code at the code file level? So that the application is more maintainable, testable, modular, reusable, and whatever other good "ables" we can come up with.

But then we take all that good design stuff we've done and bundle it up into a single deployable unit that ruins most of that good stuff that we've done...or at least that we think we've done.

We associate modularity and low coupling with maintainability of code which means the code is easy to understand and to change. It is easier to understand smaller modules and making a change in one module should affect other modules as little as possible.

But we should not automatically extrapolate these priciples to everything related to software, specifically deployment. The difference is that deployment should be designed to make things easier/better for the user, not for the developer.

Let's say that a patch is created for a product. This patch includes several fixes that end up being spread over several packages. It is better if you require the user to replace only one jar file than to replace several jar files. So, as a matter of fact, I would say that one deployment unit is more maintainable than, for instance, ten.

And the user should see and need to understand as little as possible of how the system is implemented (we could consider this deployment encapsulation , another good design principle). So presenting only one unit to the user is better than presenting one unit for each layer and thus making the user aware of the layering design. If you really want to impress a technically savvy user, then just write a document about it.

What if your product does not reside in its own separate directory, but in a common directory with other products? Think of client understanding and changing the product in such a case.

I would even propose this deployment refactoring principle: if several deployment units are always present together and in the same combination of versions in all the deployments, then they should be merged into one unit.

>>So, as a matter of fact, I would say that one deployment >>unit is more maintainable than, for instance, ten.One deployment unit is easier to deploy. It is not easier to maintain because you are not as aware of the ramifications of change if everything is bundled together.

>>So presenting only one unit to the user is better than >>presenting one unit for each layer and thus making the >>user aware of the layering design.Users don't care about the layering design, whether it be a single unit or multiple. They care about how quickly you can make changes correctly, and then deliver those changes.

>>...principle: if several deployment units are always >>present together and in the same combination of versions >>in all the deployments, then they should be merged into >>one unit. Today, I'm developing mostly Java web applications. With Java web apps, you'll still only deploy a single .war or .ear file that contains your .jar files. It's nice to know that if your app is composed of 10 different .jars, and you only change 1, when a new bug surfaces you'll know in which .jar file the bug exists. The smaller the unit, the easier (and safer) it will be to maintain. There's no doubt about that. And while you may believe today that the same combination of versions are used in all the deployments, it may not be that way tomorrow. Instead, just assemble your application from the appropriate version of separately deployable units (if they never change, then so be it). The user winds up with the same thing, and the developer winds up with something that is more manageable, flexible, and maintainable.

Even back when I was developing mostly client/server applications, where we would deploy a single executable with multiple other libraries, keeping these libraries small and focused was very useful. It helped promote the "ilities", and we took a great deal of comfort in knowing that when we patched one of these smaller libraries, we were only deploying the minimal amount necessary. If a problem surfaced after the deploy, we'd know the problem was due to the new version of the library. Had we deployed one huge unit, we could not be certain that was the case. Additionally, we'd be required to deploy that one huge unit for even the smallest of changes.