We are currently in the process of analyzing the technical requirements for a new version of a Java EE application. It's Java EE 6, but moving onto 7 is an option. Until now this was a single application, one EAR, offering well-defined functionality. However, it needs to become possible to define some functionality in a very specific way based on implementation project and/or customer.
For example, some customers are going to have very specific security constraints. A list of messages processed by a framework can be shown. For one customer, it's okay to have users see everything, but another customer would like to show only certain types of messages based on the user's group. That's an example of defining an implementation in a context-specific way, adapting the core functionality. Another possible requirement is that some customers will want to expand on the given functionality, adding new possibilities. So that's the extensible part.
Because of this, it is necessary to have an architecture that defines generic functionality but has pluggable parts, as well as a possibility for extension. For some aspects I have a rough idea of how this could be handled. This question has an answer that would work perfectly for the presentation layer, which we'll do in JSF 2: How to create a modular JSF 2.0 application?
I'm less certain about the business logic layer, security etc. My initial idea would be to define interfaces (a couple of facades) and find the implementation at run-time or deployment time. In much the same way as the service provider mechanism. A default implementation can be offered, with the possibility of defining a custom one. This does feel like a sort of Java SE solution, and I'm wondering if I'm just applying concepts that I'm familiar with from that context and if there's nothing better for EE. I think the @Alternative
annotation serves a purpose like this.
Another possibility might be to use interceptors and decorators. I'm not certain to what extent interceptors are useful outside of logging, auditing and other things that don't touch the core business logic. Decorators seem suitable for allowing an implementation to be extended with custom functionality, and perhaps also the pluggable parts.
Can someone offer some insight into which solution is best for which part of this challenge? Should I combine these methods for various parts of the problem domain? Are there possibilities I'm not seeing?
An important requirement: we want to make it possible to keep the code that is specific to a customer/project separate. We don't want to have a complete version of the application under version control for each implementation, as that would become a maintenance nightmare fast. Ideally, it would also not be necessary to build this as a monolithic EAR, but be capable of adding pluggable parts to some lib folder or deploy them separately.
Frankly speaking there are not so many good approaches to make your application modular
(I think this is the proper way to rephrase your requirements).
Please also note, there were so many discussions, failed attempts and strange designs to simplify development of modular applications, but still we have so many monolithic monsters. I've been supporting enough enterprise systems to be frightened till the end of my life.
Initial design is very important and it is not only about basic concepts like interfaces
. Please don't get me wrong. Interfaces are important, but I would prefer to use a bit different terminology -- contracts
or extension points
Let's imagine you've defined extension points
properly. What will you do next? I'd say eventually you'll implement some kind of plugin framework
. (You can spend an hour to play with JSPF and see how it simplifies development of modular applications and encourages loose coupling).
Bad news, this solution might be a bit dangerous for any production system. Mainly because complex class loading strategy (introduced by this approach) might cause memory leaks. So you'll find yourself analyzing memory dumps quite soon. Class-loaders and all related stuff became a bit complex :)
Anyway, let's assume you've solved all issues with class loading, but what about plugins/modules life-cycle? In any loosely coupled system you'll need to to strictly define how modules will interact. Plugin framework will not entirely solve this problem.
Eventually you'll come up with modules life-cycle
in order to define all important aspects. For example:
My suggestion is to avoid reinventing the wheel. OSGi might be a good solution for you. Please also note OSGi is good, mature and provides a lot stuff out of the box, but it is also somewhat complex:
So you'd definitely need some time to investigate it a bit deeply. Please also check the following : What benefits does OSGi's component system provide you?
Another suggestion is to check any existing big software product known to be good and modular (Jenkins, for example).
Update
Ok, taking into the account the discussion below I would suggest the following:
Conclusion
Sorry for the long answer, but I have to make sure there is a clear and logical point behind it. Your initial intention is 100% correct and good. It is really better to define interfaces/contracts and hide all complexity behind them. This is exactly what OOP is about.
Anyway, the most important task is not to provide good design, but preserve it over time. My suggestion is to enforce good design by using loosely-coupled approach from the same beginning. Service Locator patter is what you actually need.
It will act as a some kind of barrier to save your project and minimize spaghetti-like code. If you identify some problematic module - ok, no problem, it will be properly isolated and easy to replace.
If you have enough confidence skip Service Locator
and go for Dependency Injection
. Additional reading: Fowler on Dependency Injection vs Service Locator
If one of your goals is to keep the source code separate, I would definitely use the interface approach. You could access your code using a builder based on run-time settings and instantiate the proper class.
I have done this approach in the past, although with one code base.
You can define a permission-based design to (not) show to a user (or costumer) some messages. And extend your software functionalities using scripts like bsh or python outside the main application.
Have you investigated the Strategy Pattern? It sounds exactly like what you are looking for. It allows your code to select behaviors at run time without using instanceof or complex class inheritance. Here is an example and here is the full article:
public interface ShippingMethod {
public double getShippingCost(double weightInPounds, double distanceInMiles);
}
public class FirstClassShipping implements ShippingMethod {
public double getShippingCost(double weightInPounds, double distanceInMiles) {
// Calculate the shipping cost based on USPS First class mail table
}
}
public class FedExShipping implements ShippingMethod {
public double getShippingCost(double weightInPounds, double distanceInMiles) {
// Calculate the shipping cost based on FedEx shipping
}
}
public class UPSShipping implements ShippingMethod {
public double getShippingCost(double weightInPounds, double distanceInMiles) {
// Calculate the shipping cost based on UPS table
}
}
public class ShippingInfo {
private Address address;
private ShippingMethod shippingMethod;
public Address getAddress() {
return this.address;
}
public double getShippingCost(double weightInPounds, double distanceInMiles) {
return shippingMethod.getShippingCost(weightInPounds, distanceInMiles);
}
}
In your case, your clients can provide "pluggable" implementations for whatever view/service/model strategy you want to let them alter.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With