Framework and Application Layering Using Spring

0

An enterprise application is made up of four layers:

  1. Presentation
  2. Application or Service Layer
  3. Domain or Business layer
  4. Data Access or Persistence Layer

In ‘First Aspect of Layering Using Spring BeanFactoryLocator’, we have seen how to approach layering using spring.

One can have separate application Context for each of the above layers.

In this article, we will further modularize the code by pushing the concerns dealing with layering to a framework, so that the application module can just focus on the domain and avoid getting into the technicalities of layering.

Modularize into Framework and Application

Modularize into Framework
and Application

However the challenge lies in the framework module to allow placeholder of XML context files which the application module can override with its own set of context files. Let’s start with our example but first a bit about the setup.

This example uses the following frameworks:

  1. Maven 3.2.3
  2. Spring 4.1.5.RELEASE
  3. Eclipse  as the IDE, version Luna 4.4.1.

Dependencies

Add the following dependencies:

  1. spring-core
  2. spring-context
  3. spring-beans

Composite of Context Files

Each layer will have its own ApplicationContext definition and beanRefFactory.xml will contain the respective ApplicationContext beans with their context files. If you want to know more about the layering, please read my article ‘First Aspect of Layering Using Spring BeanFactoryLocator’ Spring BeanFactoryLocator’.

If I want to fetch a bean from a domain layer, all I have to do is use DomainContext. There will be one context class for each layer. For simplicity, we will only show the context for the domain layer here. You can fetch a domain object like below:

DomainObject1  frameworkDomain1 = (DomainObject1) DomainContext.getBean("domain1");

The bean that defines domain layer’s ApplicationContext.

	<bean id="DOMAIN"
		class="org.springframework.context.support.ClassPathXmlApplicationContext">
		<constructor-arg index="0" ref="compositeList" />
	</bean>

Note that the list of XML files that the domain layer is made up of are a composite of framework module and application module’s domain context files. This is defined by bean compositeList.

	<bean id="compositeList" class="com.javarticles.CompositeList">
		<property name="frameworkLocationList" ref="frameworkContextList" />
		<property name="appLocationList" ref="appDomainContextList" />
	</bean>

frameworkContextList and appDomainContextList are beans representing List of context XML files.

	<bean id="frameworkContextList" class="java.util.ArrayList">
		<constructor-arg index="0">
			<list>
				<value>frameworkDomainContext1.xml</value>
				<value>frameworkDomainContext2.xml</value>
			</list>
		</constructor-arg>
	</bean>

	<bean id="appDomainContextList" class="java.util.ArrayList">
	</bean>

appDomainContextList is an empty list and will be overridden by the application module.

How do we allow overriding the bean?

We use an import of another beanRefFactory.xml which will be application specific.

<import resource="classpath*:appBeanRefFactory.xml" />

Here is the complete beanRefFactory.xml.

beanRefFactory.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="DOMAIN"
		class="org.springframework.context.support.ClassPathXmlApplicationContext">
		<constructor-arg index="0" ref="compositeList" />
	</bean>

	<bean id="compositeList" class="com.javarticles.CompositeList">
		<property name="frameworkLocationList" ref="frameworkContextList" />
		<property name="appLocationList" ref="appDomainContextList" />
	</bean>

	<bean id="frameworkContextList" class="java.util.ArrayList">
		<constructor-arg index="0">
			<list>
				<value>frameworkDomainContext1.xml</value>
				<value>frameworkDomainContext2.xml</value>
			</list>
		</constructor-arg>
	</bean>

	<bean id="appDomainContextList" class="java.util.ArrayList">
	</bean>

	<import resource="classpath*:appBeanRefFactory.xml" />

</beans>

For the framework module to successfully load the beanRefFactory.xml, we will have an empty appBeanRefFactory.xml file.

Let’s make sure we can find a framework specific domain object.

SpringFrameworkContextExample:

package com.javarticles;

import com.javarticles.business.DomainContext;
import com.javarticles.business.DomainObject1;
import com.javarticles.business.DomainObject2;

public class SpringFrameworkContextExample {
    public static void main(String[] args) {
        DomainObject1  frameworkDomain1 = (DomainObject1) DomainContext.getBean("domain1");
        System.out.println(frameworkDomain1);
        
        DomainObject2  frameworkDomain2 = (DomainObject2) DomainContext.getBean("domain2");
        System.out.println(frameworkDomain2);
    }
}

Output:

Framework Domain Object1
Framework Domain Object2

Override Bean

Application module will provide the actual appBeanRefFactory.xml.

appBeanRefFactory.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

	<bean id="appDomainContextList" class="java.util.ArrayList">
		<constructor-arg>
			<list>
				<value>appDomainContext1.xml</value>
				<value>appDomainContext2.xml</value>
			</list>
		</constructor-arg>
	</bean>

</beans>

Each domain context file contains domain specific beans.

appDomainContext1.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="appDomain1" class="com.javarticles.business.DomainObject1">
    	<constructor-arg index="0" value="Application Domain Object1" />
    </bean>	
</beans>

appDomainContext2.xml:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="appDomain2" class="com.javarticles.business.DomainObject2">
    	<constructor-arg index="0" value="Application Domain Object2" />
    </bean>	
</beans>

Let’s now fetch application specific domain objects.

SpringAppContextExample:

package com.javarticles;

import com.javarticles.business.DomainContext;
import com.javarticles.business.DomainObject1;
import com.javarticles.business.DomainObject2;

public class SpringAppContextExample {
    public static void main(String[] args) {
        DomainObject1  frameworkDomain1 = (DomainObject1) DomainContext.getBean("domain1");
        System.out.println(frameworkDomain1);
        
        DomainObject2  frameworkDomain2 = (DomainObject2) DomainContext.getBean("domain2");
        System.out.println(frameworkDomain2);
        
        DomainObject1 appDomain1 = (DomainObject1) DomainContext.getBean("appDomain1");
        System.out.println(appDomain1);
        
        DomainObject2  appDomain2 = (DomainObject2) DomainContext.getBean("appDomain2");
        System.out.println(appDomain2);
    }
}

Output:

Framework Domain Object1
Framework Domain Object2
Application Domain Object1
Application Domain Object2

Download the source code

This was an example about pushing spring specific stuff to a framework module and making the application module extend the framework to plugin its own context files.
You can download the source code here: springFrameworkContextExample.zip and springAppContextExample.zip

Share.

Comments are closed.