In my very first post on this blog, I talked about service-oriented architecture (SOA), and how, while I thought it was extremely appropriate for a certain range of cases, the overhead involved in web service invocation made it very inappropriate for an equally wide, if not wider, range of cases.
Today, I want to talk a bit about an ADF 11g alternative to SOA that still gives you many of its benefits without the web service invocation overhead: reusable applications. (Next week, I’ll talk about another SOA-in-spirit mechanism that ADF 11g provides: Shared application module instances.)
ADF 11g allows you to package up an application, containing business components, page fragments, and a bounded task flow, into a JAR file that can then be used by other applications. This is really, seriously, a big deal for people who like the idea of SOA but aren’t quite sure it’s worth it for their business case.
Why? Because a bounded task flow, together with the page fragments it displays and the application module that supports it, is a lot like a service:
- It allows the user to do one “thing,” which can be a fine-grained or as coarse-grained as you want.
- You can compose it out of finer-grained “services,” and compose it into coarser-grained ones.
- You can use it again and again, in a variety of applications.
- You can pass in parameters, and get out values.
- The composition takes place using a declarative language (ADF task flows instead of BPEL, but it’s the same idea).
- You can develop it independently of other “services,” maintaining only a constant interface, seriously reducing risks from change and source control conflicts.
Moreover, there’s no web-service encoding, no need for network activity between the “services” (they all run as part of the same application, so communicate via local Java calls), no security to manage separately from your standard JAAS, and so on.
Now, obviously, this doesn’t quite get you everything SOA does:
- You have a new instance of the “service” for each user of each application. You’re reusing code here, not objects.
- These “services” can only be used in Java EE applications, unlike a web service, which can be accessed from any networked context via an XML message.
- “Publishing” one of these “services” so that it can be used by partner code involves sending the JAR file to your partner.
So for certain needs (such as easy interaction with partner code), web services are definitely quite superior to reusable applications. But for many other cases, I might say most cases where all clients of the code are going to be intra-organization, application re-use provides a very competitive alternative.
How Should I Make These Reusable Applications?
The hardest step in this process, like the hardest step in a genuine SOA application, is a purely architectural one. You need to change gears from developing stand-alone applications to building a portfolio of services. I’d suggest reading the 10.1.3 SOA Developer’s Guide Chapter 3 to get a general understanding of this process (the 11g documentation spends a lot less time on general design considerations).
Remember that this is an iterative process–you don’t need to come up with a complete and final set of reusable applications as the first step in a new top-level application. In your first development project, you’ll be deciding what your application needs to do, breaking it up into discrete subtasks, and designing reusable applications from there. Later applications are just the same except that you should search your portfolio for existing reusable applications before creating new ones.
One of these “reusable applications” (at the finest grained level, we’ll talk about composing them later) will contain the following:
- Either its own entity objects/associations (if it uses distinct database objects from any other reusable app) or entity objects/associations created in a separate “business components shared library” application and imported as part of an ADF BC library (see Section 36.7 of the Fusion Developer’s Guide for Oracle ADF).
- Either its own view objects/view links (if it uses distinct queries from any other reusable app) or view objects/view links created in a separate “business components shared library” application and imported as part of an ADF BC library.
- Its own application module, which aggregates the data model required for this specific task.
- Its own bounded task flow, and associated page fragments and page definitions.
Think about writing these low-level reusable applications as like writing very small standalone applications which just do one thing: They accept some parameters (such as a user ID) and let the user do something very simple, such as complete the payment process for an order. The only thing that really needs to be special about them is that their task flow needs to be bounded.
Remember, also, that the application module should be as simple as possible for the needs of this task. A payment process reusable application probably doesn’t need access to your product category table (even though the whole shopping process does), so its application module probably doesn’t need to contain an instance of a view object that queries that table. Granularity is your friend here.
So I’ve Got a Portfolio of Reusable Applications. What Now?
OK, so you’ve got little bitty applications that perform all the tasks your current “real” application requires. The next step is composing them into that “real” application. This can be a multi-level process: You can compose a bunch of little bitty applications that enable a little bitty task either into a finished application that enables all the use cases you’re trying to satisfy or into a slightly bigger (still reusable) application that enables a slightly bigger task, but still requires further composition.
You need to deploy each of your little applications as JAR files (as in Section 32.2 of the Fusion Developer’s Guide for Oracle ADF), and then import all needed reusable application JAR files into your “real” (or at least bigger) application (as in Section 32.3). Here’s what I recommend doing to keep everyone on the same page in terms of app versions:
- Whenever you deploy a reusable application, you should deploy it to two places: A shared classpath location for your OC4J instance(s), and a networked drive.
- For design time, all developers should share the JAR file located on the network drive (exceptions may need to be made for remote developers, who can just agree to copy down the JAR every time it changes).
- At runtime, all deployed applications will share the same copy of the JAR on the classpath.
You create a task flow and application module for your “real” application, but the task flow mostly just refers to various bounded task flows from your reusable applications, and your application module may be virtually empty. You can refer to various bounded task flows in either of two ways–by making the reusable task flow run in a region of the main page (the “region” could be the whole thing, the whole thing except for global headers/buttons/etc, or just a small component), as discussed in Section 14.3 of the Developer’s Guide, or by making it run in a separate dialog, as explained in Section 18.7.
The most important thing is to set the properties of the task-flow usage appropriately. For most uses, you want to set the following:
What does this do? Well, what these properties do if the higher-level task flow and the nested task flow both use the same application module is pretty well explained in Sections 16.3 and 18.2 of the Developer’s Guide: the task flow usage “reuses” the data controls being used by the higher-level task flow, meaning that, instead of checking out a new instance of the application module, the nested task flow just reuses the old instance, getting its connection and transaction.
But that case isn’t really what we’re talking about here. We’re talking about a nested task flow that uses a different application module from the top-level task flow. And how reuse works in that case was not clear to me until Steve Muench explained it to me directly.
If the nested task flow uses its own application module, an instance of that application module is automatically created, at runtime, as a nested instance of the top-level task flow’s application module. Nested instances of application modules don’t have their own pools, configurations, connections, or transactions–they inherit all of these from their containing application module. So there’s no additional overhead, in terms of pooling or connections, to these new application module instances.
The only time you won’t want to set these properties is if you really do want the task flow to have its own top-level application module instance, complete with its own connection and transaction. But that’s the less common case; you usually want a single transaction (or a purely serial set of transactions interrupted by commit or rollback operations) to cover an entire session with your application.
In my opinion, the ability to break up applications like this is a very big deal, especially for larger teams of developers. You really do get most of the advantages of a service-oriented model: The ability to break up work into discrete applications that can be handled by different developers, the potential for enterprise-wide code reuse, and so on, without extra transactions or extra connections, and without the overhead involved in a web service protocol.
It’s still, of course, some additional up-front work, like any architecture meant to increase reusability. If you’re charged with being the sole developer of the only web application your employer will ever have or need, this architecture model really might not be for you; it will probably make more sense just to plow through the application with a more traditional approach, rather than trying to break it down into little pieces. (When reusability is really worth it is a topic for a whole different post). But for any more extensive development effort than that (certainly if, for example, there are more than the equivalent of about 2 full-time developers working on web applications for the long term), this option has the potential to save a lot of time and hassle, in both development and maintenance.