Dec 11, 2013

Building Extendable/Flexible/Customizable J2EE Software Architecture with Spring


Software Architecture - Organization of system with components and how they interact with each other and environment. The whole system is build for some functions. When we draw a picture of this whole system, we want it to look good, we call it design, and a design can only look good if it follows some principles. The principles while tried and tested, and when we follow those principles while designing our system, we are giving guarantee in advance that system will be stable,  success and scalable.
Apart from stable, performance, and scalability, one of the important aspect about building a IT product is that it can be customized and extended.
I will discuss here an architecture, a system which is composed of multiple components/modules, and each module can be customized.

Develop a system with set of functions which will marketed as a product. Each new client can customize one or all the functions available.

I can say that system has some modules/functions; modules are integrated in one system; each module is designed in a manner that it can be extended; customization of one module doesn't affects other modules and integration of whole system; and packaging of one client doesn't includes customization's done for another.

When system designing started lot of thought process went for a long time regarding design, technologies to be used, modules, and regarding extensibility.

Its an online application. And one module will consist of some pages and controllers. And we can say a module is some page navigation's which complete one function. Like for example, a registration to a website involves some steps and end result is registration. Through a series of page navigation and some controller invocations, registration completes. And so we can consider "Registration" as one module. And then login/authentication a separate module. And one client can have separate fields mandatory while registration than another. One client can have separate pages/steps of registration than another.

Considering requirements and after some research Spring Web flow is winner for such a product -






  • Page flow of the application is visible just by looking at XML or java configuration.
  • Web flows are designed to be self contained, and thus are reusable multiple of times. 

  • Let me first discuss what is a flow and how can we visualize and implement it.

    What is a flow -

    Flow – A Sequence of steps – Part of application as a module
    Fund Transfer flow ->
    1. User chooses ‘Transfer & Payment’ from Account Summmary Screen.
    2. System will display the Fund Transfer fa├žade for user to choose an account (omit and jump to step 3 if user has only one account).
    3. System displays fund transfer input screen and asks user for beneficiary account information and transfer details.
    4. System validates the information and forwards user to confirmation page.
    5. Once user confirms the transfer order, a display-only result page appears with the updated data
    OR we can say:   Flow – Series of states: point in flow where something happens – view is displayed or action is executed




    For View, I have attached an xhtml(JSF) file, view file can be JSP or HTML or any other.



    All flow definitions used by Web Flow are added to registry.
                    webflow-config.xml

              Use the parent attribute to link two flow registries together in a hierarchy. When the child registry is queried, if it cannot find the requested flow it will delegate to its parent.
              FlowDefinitionRegistry loops through all flow registries in context and prepares a MAP with key as ID and value as FlowDefinition and stores in Cache.
              The key method is getFlowDefinition which takes a flow ID and returns a FlowDefinition for that ID.


    Dispatcher Servlet initializes the FlowHandlerMapping. On request servlet gets handler
    FlowHandlerMapping uses FlowURLHandler to get flow from URL. If FlowRegistry contains flowId, then returns a FlowHandler for flowId to Dispatcher Servle
    A request leaves the browser asking for a URL and optionally with request parameters.
    The request is first examined by DispatcherServlet.
    DispatcherServlet consults handler-mappings defined in a configuration file and selects an appropriate
    controller and delegates to it to handle the request.
    The controller applies appropriate logic to process the request which results in some information (a.k.a.
    model). This information is associated with the logical name of a result rendering entity (a.k.a view) and the
    whole is returned as a ModelAndView object along with the request back to DispatcherServlet.
    DispatcherServlet then consults the logical view name with a view resolving object to determine the actual view
    implementation to use.
    DispatcherServlet delivers the model and request to the view implementation which renders an output and sends it
    back to the browser.
    Servlet gets appropriate HandlerAdapter, invokes handlerAdapter.handle, which using flowExecuter executes the flow.

    Pink Square box is what can be extended and customized. 

    But before I discuss how I can customize the framework, I have a concept of giving each client an ID. 
    All the screen labels, messages, etc are coming from DB, and in each table I am keeping the ID as part of primary key so that message, screen label can customized according to each client. 

    Customizing Screen labels, messages, etc. for each client- 
    So, Like I want different error message for different clients for same business failure. 
    In MESSAGE table I will have entry with KEY, CLIENT_ID, and MESSAGE.
    And inside code I set the Client ID in application context, to that all queries include the CLIENT_ID as search criteria and appropriate message is picked. 

    Customizing/Extending Product feature for each client - 

    • Every feature is associated with a flow. 
    • All flows have ID. 
    • Any feature has a flow. 
    • Any feature which is not there in product is developed and made part of product while developing for client, which saves money also(upgrading product for free)
    • For any customization of any feature for any client, we extend the flow. 
    • We add Client_ID to the flowId when registering the extension flow in flow-registry. 

    So, if we have a flow with ID - register. 

    • We want to customize this flow for client abc. 
    • We have given client abc as CLIENT_ID as ABC.
    • We create a flow with ID as ABCregister which will extend the flow register. 
    • Only new states or changed states goes in extended flow(ABCregister), and rest things are picked form base flow(register). 

    How it works - 

    1. Both flows exist in the registry. 
    2. Registry is a HashMap with Key as flowID. 
    3. When user clicks on "Register" button on screen, the request URL contains the flowID, i.e. register. 
    4. System searches in the HashMap for the key, but it searches in order - 
    5. if(CLIENT_ID+flowID) exists return value against CLIENT_ID+flowID
    6. else return value against flowID.
    7. By default flow from the base product will be returned, unless we have extended the flow and registered that also. 
    8. If extended flow exists, then it will get preference. 

    Inside flows we refer the view state, which can be customized for each client.

    So, in flow I refer registerInput.html, and I want to customoze it for client abc. 
    I have registerInput.html as path com/product/register/registerInput.html
    I will place customized file at location - 
    com/product/register/abc/registerInput.html, also note my flow file path is - 
    com/product/register/register-flow.xml

    And I have wrote a custom resource(file) loader with logic - 
    Pick Client ID from application context. and search for - 
         clientId/viewName if not found then return - 
         viewName


    Bean extension –
              Extension architecture uses aspect class MultiClientExtensionLoaderAspect which contains a single piece of advice - “tryToInvokeCustomized”
              @Around("extensionPoint() && !isExtension() && !ignore()")
    public Object tryToInvokeCustomized(ProceedingJoinPoint pjp) {
                Object target = pjp.getTarget();
                Object extensionBean = findCustomizationForClient(target);
                if (extensionBean != null) { // if extension is found;
                    return invokeExtension(pjp, extensionBean);
                }
                Object retVal = pjp.proceed();
                return retVal;
         }
    findCustomizationForClient(target){
    String bean = target.getBeanName();
    extensionBean = extensionRepository.getExtension(bean groupId+businessID);
            if(extensionBean == null){
            extensionBean = extensionRepository.getExtension(beanName, groupId);
            }
    }
    Map> beanRepository( beanName, )

    So, my around advice works for all methods be it in action class, service class, or anywhere. It will look for first if any extended method otherwise invoke form base product. 
    Share:

    No comments

    Post a Comment

    Comment

    © Shift, ShEkUP, Shape, and Surprise | All rights reserved.
    Blogger Template Crafted by pipdig