Chapter 8 – Reference Implementation

Thus far, we’ve spent the majority of Part 1 making the case for modularity. Before we delve too deeply into the patterns, let’s witness first-hand the benefits of a modular architecture.

8.1 – Why No OSGi?

The example that follows doesn’t use OSGi. One would think that if I’m going to modularize my system, I’d want to use a proven module framework, such as OSGi. There are a couple of reason I chose not to.

  • While OSGi is a module system, OSGi will not help you design more modular software. In other words, it doesn’t help us with the design paradigm. Among other things, designing modular software requires that we understand the weight and granularity of individual modules (discussed in Chapter 6), and use the right techniques to manage the coupling between modules (dependencies are discussed in Chapter 5). In other words, designing good software is our job. Tools and technologies may help, but make no guarantee. For more on the differences between the runtime and development model, see Chapter 3.
  • Many of us aren’t able to leverage OSGi today because the platforms and languages we use don’t support it. I want to use the same tools and techniques that we can leverage in the enterprise right now.
  • Mostly though, I didn’t want to shroud the examples in technology. Instead, I want to focus on pure modularity. For the interested reader, you can view the final version of the system, in all it’s OSGi glory, in Appendix X. You’ll find that the module structure hasn’t changed. Instead, OSGi allows us to take advantage of the runtime benefits of modularity, such as hot deployments.

In general, I want to focus this chapter on the design paradigm, not the tools and technologies. So instead of answering, “How do I use OSGi?”, I want to focus on “How do I develop a system with a modular architecture”. Hopefully, that resonates with you.

8.2 – Let’s Get Started – The System

Interestingly, I’ve found that when designing modular software, it’s tough to identify the modules early in the lifecycle. Instead, shifts typically occur, and as things unfold, the modules become more apparent as development progresses. With a SOLID OO design, it’ll make it much easier to move things around and create new modules. So to start, while the system is small, I favor coarser-grained and heavier-weight modules.

As specific needs emerge, we’ll break larger modules out into a bunch of finer-grained and lighter-weight modules that address specific functional and non-functional needs. Remember, the material in the first six chapters describe the essence of my motivation here. It’s deep and very interesting, and impacts how we understand the software, maintain the system, reuse software entities, and more. If you haven’t read any of the previous material, you might want to check it out now, or you can wait and see what I’m talking about, as we’ll experience this phenomenon as we move through the exercise.

Here’s a simple, high-level description of the system we’ll develop. It’s the common bill payment sample system often used.

We’ve been asked to develop a system to handle payment of of bills. Prior to paying the bill, the system should apply a discount to the bill in an amount that has been negotiated with the payee (we call this the process of auditing the bill). Applying this discount is a fairly complex process, and a 3rd party vendor has been commissioned that will apply this discount. Additionally, we must integrate with a legacy financials system that must be fed payment information for reconciliation.

We’ll flesh out additional details as development progresses.

8.3 – Version One

The initial version for this system uses Struts as the web framework, and packages everything into a single WAR file. The initial class diagram can be seen in Figure 1. It’s not a complete class diagram, but it does show the main set of classes. I’ve greatly simplified the system for purposes of example.

InitialBillPayClassDiagram

Figure 1: Initial Version Class Diagram

As you can see, there are some Action and ActionForm classes that leverage Struts. There are also a couple of JSP you don’t see here. A Customer has a list of Bills, and each Bill has a reference to an AuditFacade and Payment class. The AuditFacade integrates with the 3rd party vendor software that applies the discount, and the Payment class integrates with a legacy financial system. Of particular interest, note the bi-directional relationship between Bill and the AuditFacade and Payment classes – a sure sign of a problem that will haunt us later. But don’t worry, we’ll fix it.

For the sake of simplicity, I’ve hardcoded the database into the data access layer, which is represented by the Loader interfaces. This way, you can experiment with the system without running any scripts to create the database. If you are compelled to do so, feel free to add a real database backend. It wouldn’t be very difficult, but it’s not what I want to focus on here.  While we’ll show some code snippets as the example progresses, I have setup a Google Code Repository that shows each step in the evolution. That repository can be found at http://code.google.com/p/kcode/source/browse/#svn/trunk/billpayevolution/billpay, and each of the subprojects represent a single step in the example.

8.4 – First Refactoring

So packaging everything up into a single WAR file for deployment certainly isn’t modular. In most systems we develop, we try to design layers that encapsulate specific behaviors and isolate certain types of change. Typical layers include a UI layer, a business or domain object layer, and a data access layer. In this system, we have these three layers. The Struts action and Form classes, along with the JSP, represent a part of the UI layer. The UI layer is represented by the red classes in Figure 1. The Customer, Bill, Name, AuditFacade, and Payment form the business object layer, and the Loader classes form the data access layer. These classes are shown in blue in Figure 1. Now, here’s a key statement that you need to take with you.

If I truly have a layered system, then I should be able to break out each layer into a separate module where modules in the upper layers depend on modules in lower layers, but not vice versa.

If you try this for one of your systems, it’s likely you’ll find it’s not so easy. Most development teams feel they have a layered architecture, but in reality, they don’t because somewhere deep within the bowels of the system lies an import or reference to a class higher up in the food chain that we aren’t aware of.

PhysicalLayersRefactoringRed

Figure 2: Applying the Physical Layers Pattern

In fact, if I really do have a layered system, then I shouldn’t have to change anything other than my build script to break the layers out into separate JAR files. If I do have to change more than a build script, then I didn’t have a layered system to begin with, and I should perform some architectural refactoring to clean things up. Anyway, the end result is relatively simple to understand. No code changes. Only a build script change so that certain classes are allocated to specific modules. The structure is shown in Figure 2. This refactoring was an example of applying the Physical Layers pattern.

Listing 1 illustrates a portion of the initial build script where all of the classes were bundles into the WAR file. Listing 2 shows the changes we’ve made to the build script to create modules for the various layers. Here, we only show the business object layer. We created a new build target where we create the module and then added a new line to the dist target where that module is now included in the WAR file.

<target name="compile" depends="init">
   <javac srcdir="${javasrc}:${testsrc}" destdir="${build}">
      <classpath refid="project.class.path"/>
     </javac>
</target>

<target name="bundle" depends="dist">
   <mkdir dir="${deploy}"/>
   <war destfile="${deploy}/billpay.war" webxml="WEB-INF/web.xml">
       <fileset dir="jsp"/>
       <webinf dir="WEB-INF">
          <exclude name="web.xml"/>
          <exclude name="lib/servlet-api.jar"/>
       </webinf>

        <classes dir="${build}"/>
    </war>
</target>

Listing 1: Initial Build Script

<target name="compile" depends="init">
   <javac srcdir="${javasrc}:${testsrc}" destdir="${build}">
      <classpath refid="project.class.path"/>
   </javac>
</target>

<target name="dist" depends="compile">
    <mkdir dir="${bindist}"/>
    <jar jarfile="${bindist}/bill.jar" basedir="${build}" excludes="com/extensiblejava/bill/test/**, com/extensiblejava/ui/**"/>
</target>

<target name="bundle" depends="dist">
   <mkdir dir="${deploy}"/>
   <war destfile="${deploy}/billpay.war" webxml="WEB-INF/web.xml">
      <fileset dir="jsp"/>
      <webinf dir="WEB-INF">
          <exclude name="web.xml"/>
          <exclude name="lib/servlet-api.jar"/>
      </webinf>
      <lib dir="${bindist}" excludes="test.jar"/>
      <classes dir="${build}" includes="com/extensiblejava/ui/**"/>
   </war>
</target>

Listing 2: Build Script with Physical Layers

8.4.1 – Wrapping Up and Getting Ready for the Next Refactoring

This first refactoring was quite simple, but has significant implications. Foremost, it proves that my class level architecture was decent. I was able to break the system out into modules for the various layers without having to change a bunch of code. Really, that’s the reason why it was so simple…because the design was decent. Had there been violations in the layered structure, it would have been significantly more difficult pulling off this refactoring because I would have been forced to remove the unwanted dependencies.

Yet, as we’ll see, the existing design may meet the needs of today, but it’s going to have to evolve as change emerges. In the second refactoring, we’ll take a look at what we need to do to integrate with another auditing system, and how modularity can help us do this. And as we progress, the amazing transformation of a system lacking modularity to a highly modularized version will unfold. In the next couple of steps, we’re going to apply two refactorings using two different modularity patterns – Abstract Modules and Acyclic Relationships. First, we’re going to separate the bill and audit functionality out into separate modules so we can manage (develop, deploy, etc.) them independently. Second, we’re going to remove the cyclic dependency between these two modules.

8.5 – Second Refactoring

In the initial class diagram, the Bill class has a bi-directional relationship to the AuditFacade class. This design has two fundamental flaws. The Bill is tightly coupled to the concrete AuditFacade class and the relationship is bi-directional. Bad all around! This can be seen in the following code snippet in Listing 3 illustrating the Bill’s audit method.

public void audit() {
   AuditFacade auditor = new AuditFacade();
   this.billData.setAuditedAmount(auditor.audit(this));
   this.persist();
}

Listing 3: Audit method of the Bill Class

Notice that the audit method actually creates the AuditFacade, calls the audit method, and passes a reference to Bill. Ugly. Let’s clean this up a little bit. While there are obvious technology reasons why we need to clean this up, there is also a motivating business force.

The system needs to go live with the current vendor’s auditing system, but the business has indicated that they aren’t renewing the contract with the vendor and are in ongoing negotiation with another vendor. The contract expires in 6 months, but we deliver the initial version of the system in 3 months.

So, three months after deployment, we know we’ll need to swap out auditing systems.

We’re going to apply the Abstract Modules pattern, which states that we should depend upon the abstract elements of a module. We’ll start by refactoring the AuditFacade class to an interface and create a separate AuditFacade1 implementation. This solves the first half of our problem – the tight coupling between the Bill and AuditFacade implementation. The result is the class diagram shown in Figure 4.

AbstractModulesClassDiagram

Figure 4: Refactoring AuditFacade to an Interface

Of course, the Bill can no longer create the AuditFacade implementation. Doing so would compromise the work we’ve done since Bill would still be coupled to AuditFacade1. To deal with this challenge, the AuditFacade interface is now passed into the Bill’s audit method, allowing us to swap out AuditFacade implementations. The modified audit method is shown in Listing 4.

public void audit(AuditFacade auditor) {
   this.billData.setAuditedAmount(auditor.audit(this));
   this.persist();
}

Listing 4: The New Audit Method Accepting the AuditFacade Interface.

If we take this flexible class structure and continue to deploy in the single bill.jar module, we have the flexibility to swap out AuditFacade implementations at the class level, but we’re still required to package everything up into a single bundle and deploy it as a single module. So what we really must do is separate the audit functionality out into a module separate from the bill. Separating the AuditFacade interface and AuditFacade1 implementation out into separate modules results in the diagram illustrated in Figure 5. Here’s where the bi-directional relationship between Bill, the AuditFacade interface, and AuditFacade1 implementation rears it’s ugly head. We have a cyclic dependency between our bill.jar and audit.jar modules. Remember our discussion from Chapter 5 – excessive dependencies are bad and cyclic dependencies are especially bad. We need to fix this problem.

AbstractModulesRed

Figure 5: Abstract Modules

This change will also be reflected in the build file that packages up these modules, as shown in Listing 5, where we can see the audit.jar module created.

<target name="dist" depends="compile">
   <mkdir dir="${bindist}"/>
   <jar jarfile="${bindist}/audit.jar" basedir="${build}" includes="com/extensiblejava/audit/**"/>
   <jar jarfile="${bindist}/bill.jar" basedir="${build}" excludes="com/extensiblejava/bill/test/**, com/extensiblejava/ui/**"/>
</target>

Listing 5: The Build Script Creating the Audit.jar Module

8.6 – Third Refactoring

Since cyclic dependencies are so bad, we need to remove the cyclic dependency between the bill.jar and audit.jar modules. To do this, we’ll apply the Acyclic Relationships pattern, which states that module relationships must be acyclic. We’ll introduce an additional abstraction, called Auditablethat our Bill class implements. Upon applying this little trick, we can see that we have now removed the bi-directional relationship between Bill and the AuditFacade interface, as shown in Figure 6.

AcyclicModulesClassDiagram

Figure 6: Acyclic Relationships between Classes

Whereas previously the AuditFacade accepted a Bill to the audit method, it now accepts an Auditable type. The new AuditFacade interface can be seen in the code snippet shown in Listing 6.

package com.extensiblejava.audit;
import java.math.*;
public interface AuditFacade {
   public BigDecimal audit(Auditable auditable);
}

Listing 6: AuditFacade Interface

The Bill will pass itself to the AuditFacade interface as an Auditable type. The key element at this point is how we allocate these classes to their respective modules. Here’s a simple rule to abide by when determining how to allocate classes and interfaces to modules.

Interfaces should be closer to the classes that use them, and farther away from the classes that implement them.

The Extensibility Patterns discuss this idea in more detail and we’ll leverage this concept later to address another problem that has surfaced. But applying this rule now, it’s clear that Auditable should be bundled with the AuditFacade interface, not in the same module as the Bill class. While we haven’t talked about the new financial.jar module, we applied a similar refactoring to the Payment class by implementing a Payable interface, and breaking it out into a separate module. Allocation of classes to modules is done at build time, so after modifying our build script, we have the module structure as shown in Figure 7.

AcyclicModulesRed

Figure 7: Acyclic Relationships

As I’m sure you’ve noticed at this point, we broke our rule above and bundled the AuditFacade1 implementation close to the interface it implements, which is the other problem that has surfaced. The problem, however, is that if we apply our rule in this situation, we’d bundle the AuditFacade interface and Bill class in the same bundle, which won’t work since it would result in the cyclic dependency between the bill.jar and audit.jar modules that we’ve worked so hard to remove.

8.6.1 – Wrapping Up and Getting Ready for the Fourth Refactoring

Since our last recap, we applied two separate refactorings. The first was to separate the bill and audit functionality out into separate modules. The second was to remove the cyclic dependency between the bill and audit modules.This offers us decent flexibility. We can introduce a new AuditFacade implementation, knowing that we’d only need to rebuild the audit.jar module. We can test the audit.jar module independent of any other module because it doesn’t have any outgoing dependencies. And if we wanted to, we could deploy the audit functionality separately (ie. we can reuse it elsewhere). So overall, some decent progress.

But some problems remain. If we really want the ability to swap out Audit systems, bundling the AuditFacade interface and AuditFacade1 implementation into the same module doesn’t give us the flexbility we need. While we can easily create a new AuditFacade implementation and allocate it to the audit.jar module, this requires us to redeploy audit.jar unnecessarily. In the next post, we’ll explore how we can change the module structure to allow a new AuditFacade implementation while also allowing the existing AuditFacade1 implementation to be removed from the system, and a new module to be deployed. Thus far, we’ve applied the following patterns:

  • Physical Layers
  • Abstract Modules
  • Acyclic Relationships

And while we haven’t specifically mentioned it, we could also easily apply the following at this point:

  • Levelized Modules
  • Levelized Build

8.7 – Fourth Refactoring

At this point, if we want to deploy a new AuditFacade implementation, we have two options. We can bundle it in the existing audit.jar module or deploy it in a new module. The second approach offers us some flexibility because we wouldn’t need to modify any classes in the existing audit.jar module. Unfortunately, we cannot remove the existing AuditFacade implementation because it’s bundled in the same module as the AuditFacade interface. Obviously, if we deploy a new AuditFacade implementation, we need the interface. Additionally, if both of the implementations of AuditFacade live in the same module, we can’t rip one out (ie. uninstall) when we add the other. In other words, because they are in the same module, they can only be managed together.

To solve this tricky little challenge, we’re going to apply the Separate Abstractions pattern, which states that we should separate abstractions from the classes that realize them. We actually applied this pattern when allocating the Auditable interface to the audit.jar module, but we didn’t apply it to the AuditFacade1 class. So we’ll apply it to the AuditFacade1 implementation, as well as a new AuditFacade2 implementation. First, we have to answer the following question.

Where do we put the AuditFacade interface so that new implementations of the interface can be managed separately from the interface and other implementations?

Our answer can be found by looking at the module structure shown in Figure 9. Note that I’ve also included the financial.jar module in this diagram, which was actually introduced in a previous step when we introduced the Payable interface. We’ll use this soon, but let’s ignore it for now.

SeparateAbstractionsRed

Figure 9: Separate Abstractions

We separate the AuditFacade interface out into its own module – auditspec.jar.The AuditFacade1 and AuditFacade2 implementations are also placed in their own modules. Aside from moving a few things around, no real coding changes are necessary beyond the new AuditFacade2 implementation we’ve created. Note that we’ve also separated each implementation out into different packages to avoid splitting packages across modules. After adding this new class, we modified our build script to allocate the classes to the appropriate modules.

8.7.1 – A Note on the Benefit of OSGi

At this point, we have increased the architectural flexibility of our system. The AuditFacade implementation classes are allocated to separate modules, allowing us to manage the two independently. From a maintenance perspective, we can work on each of the implementations separately, resting assured that changes to one implementation won’t negatively impact changes to the other. We can also test each independently and reuse each independent of the other.

This change also increases the resiliency of the bill.jar module since it’s no longer tightly coupled to any AuditFacade implementation. We can test the bill.jar module using only the auditspec.jar module by creating a mock implementation of AuditFacade for testing purposes. While the billpay.jar module is still dependent on the financial.jar module, we’re going to solve that problem in the next refactoring. In general, we have completely eliminated the dependencies between the bill.jar module and the AuditFacade implementations in the audit1.jar and audit2.jar modules. Remember, at the beginning of Section 7.6, these modules containing the bill and audit behavior had a cyclic relationship.

While this example doesn’t leverage OSGi, it is important to point out the benefit that OSGi can provide. Because we don’t have a runtime module system we still need to deploy these modules within the WAR file. Changes to any of the modules still requires that we redeploy the entire WAR, even though it’s possible to only rebuild the modules that have changed. The presence of OSGi, however, brings the same degree of flexibility to the runtime that we have at development time, which we discussed in Chapter 3. With OSGi, we would be able to install and uninstall the audit1.jar and audit2.jar modules without redeploying the entire system. Appendix B shows us how OSGi allows us to do this.

8.7.2 – Wrapping Up and Getting Ready for the Next Refactoring

So we’ve made quite a bit of progress from our initial version of the system, which lacked a modular architecture. By breaking the system out into modules, we ease the maintenance effort and increase overall system flexibility. In the next refactoring, we’re going to to turn our attention to the financial.jar module that focuses on payment. Recall that in the third refactoring in Section 7.6, we separated the Payment class out into a separate module and decoupled Bill and Payment through the Payable interface. In the next step we will examine how we can decouple the bill.jar module from the financial.jar module so bill.jar can be managed, tested, and deployed independent of financial.jar.

Soon, we’re going to focus on the financial.jar module and try to decouple bill.jar from financial.jar so the two are completely independent of each other. I think you’ll really like this one…But first, some additional housekeeping.

8.8 – Fifth Refactoring

Before we decouple the bill.jar module from the financial.jar module, we’re going to talk about exceptions. In general, we need to answer the question.

Where do exceptions belong?

Let’s introduce an AuditException that is thrown if an error is encountered. The Collocate Exceptions pattern says that exceptions should be close to the classes (or interfaces) that throw them. Since the AuditFacade interface throws the exception, as shown in Listing 7, we should put the AuditException in the same module as the AuditFacade interface, which is the auditspec.jar module. Note that if we put the exception anywhere else, we’d create an unwanted dependency between modules.

package com.extensiblejava.audit;

import java.math.*;
public interface AuditFacade {
   public BigDecimal audit(Auditable auditable) throws AuditException;
}

Listing 7: AuditFacade Method that throws an AuditException

8.9 – Sixth Refactoring

The previous refactoring was pretty simple. This next refactoring is rather interesting. Here’s the driver behind this next step.

A new requirement has emerged, and we need to use the bill.jar module in another system. In this other system, we don’t need the financial.jar module to make the payment. We just want to use the bill.jar functionality. So, what do we do?

First, recall our initial version of the class structure shown in Figure 1. While we didn’t spend much time discussing it, in Section 7.6, we introduced a Payable interface and  created the financial.jar module. After we finished fourth refactoring shown in Figure 9. Our goal now is to eliminate the dependency between the bill.jar and financial.jar modules so that we can deploy the bill.jar module without the financial.jar module. We’re going to apply a little trick called escalation, which is discussed in more detail in the Acyclic Relationships pattern. Essentially, we’re going to escalate the dependency to a higher level module. First, let’s take a look at the new class structure that’s going to allow us to remove the module dependency.

IndependentDeploymentClassDiagram

Figure 10: Independent Deployment

As shown in Figure 10, we see the new class structure that’s going to allow us to decouple the bill.jar and financial.jar modules. Things are starting to get a bit tricky here, so before we allocate these classes to their respective modules, let’s walk through the steps to illustrate how these classes will callaborate to give us what we need.

We start by passing a BillPayAdapter instance to the Bill as a BillPayer type. The BillPayAdapter is going to manage the relationship between Bill and Payment. When the pay method on Bill is called, it will turn around and invoke a generateDraft method on BillPayAdapter. The BillPayAdapter invokes a generateDraft method on Payment, passing itself in as a Payable. This allows the Payment to callback on the Payable and invoke the appropriate method (either getAmount or getAuditedAmount) on BillPayAdapter. The BillPayAdapter can now query the Bill to obtain the audited amount and pass it back to the payment. Once the Payment has this amount, it can make the payment.

A fairly complex callaboration, but enough to ensure we can decouple the bill.jar module from the financial.jar module. The code for the new BillPayerAdapter class, which mediates (hmm..possibly naming it a mediator would have been better, huh?) the relationship between Bill and Payment, is shown below.

public class BillPayerAdapter implements BillPayer, Payable {
   private Bill bill;
   public BillPayerAdapter(Bill bill) {
      this.bill = bill;
   }

   public BigDecimal generateDraft(Bill bill) {
      Payment payer = new Payment();
      return payer.generateDraft(this);
   }

   public BigDecimal getAmount() {
      return this.bill.getAmount();
   }

   public BigDecimal getAuditedAmount() {
      return this.bill.getAuditedAmount();
   }

Listing 8: The BillPayerAdapter Class

Now it’s time to allocate these classes to the appropriate modules. The key decision here is where we place the BillPayerAdapter class. Because this controls the Bill and Payment interaction, we can’t put it in either of those two modules. In fact, we’re going to create a new module that contains the BillPayAdapter. The new module, which we’ll call billpay.jar, will depend on both the bill.jar and financial.jar modules, as shown in Figure 11. This refactoring is an example of the Indpendent Deployment pattern, which states that modules should be independently deployable units. That’s exactly what we’ve done.

IndependentDeploymentRed

Figure 11: Independently Deployable Modules

For the most part, our system is complete now. But we do have one nasty little problem that we haven’t solved yet. We have two different AuditFacade implementations, but the current system hardcodes creation of AuditFacade1 within the AuditAction class. All this flexibility compromised by a single line of code. Unnerving how that happens! We really do have to architect all the way down.  Let’s fix this problem.

8.10 – Seventh Refactoring

The problem of creating instances is pretty common, so the solution is relative easy. There are a lot of different ways to do this, but we’re going to take the easiest route (and not the most flexible mind you) by creating an AuditFacadeFactory class. This refactoring is an example of the Implementation Factory pattern, which states that we should use factories to create a modules implementation classes. Now, instead of the AuditAction creating the AuditFacade implementation, it invokes the factory and is returned an instance, as shown in Listing 9. An alternative, and possibly a better solution, would have been to inject the appropriate AuditFacade implementation into the AuditFacade using a dependency injection framework such as Spring.

try {
   bill.audit(AuditFacadeFactory.getAuditFacade(bill));
   request.setAttribute("bill",bill);
   return mapping.fineForward("success");
} catch (AuditException e) {
   throw new ServletException(e);
}

Lisiting 9: The AuditFacade Invoking the AuditFacadeFactory Class

8.11 – The Postmortem

We’ve made considerable progress. Amazing really. From a single monolithic application to a fully modularized architecture with considerable flexibility. But there are a lot of very interesting takeaways that we haven’t talked about yet. A number of positive side affects have resulted that aren’t immediately obvious. In this section, we’ll take a more in-depth look at the impact of modularity.

Through this series of refactorings, we made considerable progress in modularizing the application using a few of the modularity patterns. The diagram in Figure 12 illustrates the progress we’ve made. We started with everything bundled into a single WAR file and wound up with a highly modularized system that satisfied the evolutionary requirements. Aside from the many advantages we spoke about in each post, I want to take a moment to explore a few other thoughts.

FinalRefImpSolution

Figure 12: Modular Architecture

8.11.1 – A Note On Module Testing

If you’ve explored the system by getting the code from the Google code repository, which we discussed earlier and can be found at, http://code.google.com/p/kcode/source/browse/#svn/trunk/billpayevolution/billpay, you’ll notice that there are a corresponding set of test modules for each module that we’ve created. These can be found in the bin directory after building the final version of the system. Like we do with unit testing, I’ve tried to create a test component for each module in the system. Unfortunately, there’s a flaw in the billtest.jar module.

Similar to unit testing, where we create mocks and stubs to avoid undesirable dependencies, an ideal test module shouldn’t pull in other modules that contain implementation classes. Instead, we should create mocks or stubs to avoid this situation. In other words, a test module should only be dependent on the same set of modules as the module it’s testing. The Test Module pattern discusses this further.

Unfortunately, the billtest.jar module breaks this rule by using the AuditFacade implementations to perform the tests. That means the billtest.jar module is also dependent on the audit1.jar and audit2.jar modules, but the bill.jar module is not. So billtest.jar is really a module integration test, not a module unit test. It could easily be fixed by creating a mock AuditFacade implementation that lived in the billtest.jar module.

This begs another question….

How do we keep track of module relationships so that we recognize when something bad like this happens?

Even for small systems, without a module system like OSGi, it can be incredibly challenging.

8.11.2. – A Note On Managing Modules

Modularizing a system on a platform that doesn’t support modularity is challenging. Modularizing a system on a platform that does support modularity is challenging! One of the greatest challenges is in managing module dependencies. Tracking the dependencies between modules is quite difficult.

This is where module systems like OSGi really help by enforcing the declared module dependencies. In Java today, and most platforms used in the enterprise today, there is no module system available so there is nothing to help enforce modularity. The first unwanted dependency that creeps into our system compromises architectural integrity. This is where a tool like JarAnalyzer can be helpful. You can find JarAnalyzer at http://code.google.com/p/jaranalyzer/. By incorporating JarAnalyzer into the build script, we’re able to more easily manage the dependencies between modules.

Figure 13: JarAnalyzer Visual Output

JarAnalyzer has two output formats. The first is a GraphViz compliant dot file that can be easily converted to an image showing module relationships. The image shown in Figure 12, which includes the test modules, clearly illustrates the problem with the billtest.jar module discussed above.

As can be seen, the bill.jar module has only a single outgoing dependency on the auditspec.jar module. So the module that tests the bill.jar module should not be dependent on any other modules, either. However, if you look at the billtest.jar module, you’ll see that it depends upon the audit1.jar and audit2.jar modules. So instead of using a mock or stub to test the bill.jar module, I got lazy and used the various AuditFacade implementations. Look at a few of the other modules, and you’ll discover that none include additional dependencies beyond the dependencies already present within the modules they test.

The second output format for JarAnalyzer is an html file that provides some key design quality metrics, as well as listing the dependencies among modules. Essentially, it’s a textual view of the same information provided by the visual diagram. I’ve included the Summary header of the JarAnalyzer report for the system, which can be seen in Figure 14. You can also browse the complete JarAnalyzer HTML report for the final version of the system. There is also a version that omits the test modules.

Figure 14: JarAnalyzer HTML Output

Look at the auditspec.jar module. Note that it has 8 incoming dependencies (afferent coupling) and 0 outgoing dependencies (efferent coupling). It’s abstractness is 0.67 and Instability is 0.00. This is a pretty good sign. Why? It’s instability is very low, implying it’s highly resistant to change. It possesses this resistance to change because of the large number of incoming dependencies. Any change to this module may have serious implications (ie. the ripple effect of change). But because it’s quite abstract, it’s less likely to change than a module with a lot implementation classes. The Distance for the module is 0.33 (ideal is 0.00), so we’re not far from where we ideally want to be.

In case you’re wondering about all these metrics I’m rambling about, you might want to take a look at the Martin Metrics. In general, without a utility like JarAnalyzer (or a module framework like OSGi), it would have been incredibly difficult to manage the modules composing this system.

8.11.3 – A Note on Module Reuse

The reuse/release equivalency principles states that the unit of reuse is the unit of release. Modules are a unit of release, and therefore are a unit of reuse. Naturally, the devil is in the details, and we’re going to discuss these details here.

In Chapter 6, we spoke of the tension between reuse and use. That tension is evidently at play here. Earlier versions of the system had coarser-grained modules that were easier to use but more difficult to reuse. As we progressed, we broke these coarser-grained modules out into finer-grained modules, increasing their reusability but decreasing their ease of use. A perfect example of this is the bill.jar module. In the final version, it was quite reusable, since it was only dependent on the auditspec.jar module. However, this came at the price of useability.

To elaborate a bit more. In the sixth refactoring in Section 7.9, we decoupled the bill.jar and financial.jar modules so the two could be deployed independently (ie. increase reuse). But the runtime structure still has some dependencies. In order to reuse bill.jar, we need a BillPayer type. While an alternative BillPayer implementation could be created, the existing implementation is the BillPayAdapter in the mediator.jar module, which also has a relationship to the financial.jar module. This means that to use the bill.jar module without the mediator.jar and financial.jar modules would require a new consuming module to implement the BillPayer interface.

So what do we do if we want to break this runtime coupling? We should move the pay method on the Bill up to the BillPayAdapter class, and get rid of the BillPayer interface. Now the Bill class has no dependency on the BillPayer interface, but it also can’t make payments. Every action has an equal an opposite reaction, heh?

8.11.4 – A Note on The Build

The build was a key element in helping enforce modularity (note: JarAnalyzer helped me manage module relationshps; the build enforced module relationships). Even a framework such as OSGi is only going to manage module relationships at runtime. We talked about the design paradigm in Chapter 3, and it’s why we need really good tools that help us design more modular software. It’s our responsibility to craft the modules, and the build is one way to help put in place a system of checks and balances that help enforce modularity before discovering at runtime that one module has a relationship to another. As part of the third refactoring in Section 7.6, we could have easily refactored our build script to a Levelized Build. If you take a look at the code in the Google code repository, you’ll notice how the build changes after we applied the Acyclic Relationships pattern.

This means that as we build each module, we include only the required modules in the build classpath. We speak more about this when discussing the Levelized Build pattern. To elaborate on this a bit, we build the modules with fewer outgoing dependencies first, following by those that are dependent on the modules previously built. For example, when we build the auditspec.jar module, we include nothing else in the build classpath because the auditspec.jar module doesn’t require anything. When we build the audit1.jar module, we’ll only include the auditspec.jar module in its build classpath. This pattern recurs throughout the remainder of the build scripts. Introducing a module dependency that violates the dependency structure enforced by the build results in a failed build.

8.11.5 – A Note on Object Orientation

The way we managed, massaged, and modified module relationships was through object-oriented techniques. By introducing interfaces and abstraction and allocating them to their respective modules, we were able to significantly change the module structure of the system. While we used object-orientation to do this, that is not a prerequisite. We could just as easily have used other techniques, such as aspects (AOP). The key is that we are managing the dependencies between modules.

8.11.6 – The Final Wrap

I recognize that this was a pretty lengthy and involved tutorial. But it’s necessary to leverage a relatively involved example to show the positive affect of a modular architecture. It also shows how we can use many of the modularity patterns, which we’ll now turn our attention towards. Again, for the version of the system that uses OSGi, see Appendix B.