Spring Integration

I have been really impressed by the Spring Integration project recently so I decided to write a few words about it.

Just to make a quick introduction:

Spring Integration can be described as an extension of the Spring programming model which supports Enterprise Integration Patterns.

It enables lightweight messaging within Spring-based applications and supports integration with external systems via declarative adapters. Those adapters provide a higher level of abstraction over Spring’s support for remoting, messaging, and scheduling.

In my opinion the biggest advantage of the project is fact that it provides simple model to build enterprise applications while maintaining separation of concerns at the same time. It results in testable, maintainable and easy to change code. Code in services is responsible only for business logic, while executing services’ methods, synchronous/asynchronous communication, etc. is described in configuration and handled by the framework.

Another aspect of the project I really like is fact that it introduces messaging model into your application. I mean introducing messaging into communication between modules/layers or even particular services in the application. Many people pigeonholes messaging as way to integrate with external systems. I agree that it perfectly suits into such scenarios but why not use it to integrate modules and services which belongs to the same application? Spring Integration provides us with clear and simple tooling to support such architecture.

Let’s move to an example.

Consider simple case where we have a queue of credit card transactions which are to be processed. Transaction under 10000$ are processed by one service while transactions over 10000$ are processed by another one.

What we need is Transaction class:

public class Transaction {
  private BigDecimal amount;

  public Transaction(BigDecimal amount){
    this.amount = amount;
  }
  public BigDecimal getAmount(){
    return amount;
  }
}

Transactions under 10000$ processing service:

public class TransactionProcessingService {
  private static Logger logger = Logger.getLogger(TransactionProcessingService.class);
  public void process(Transaction transaction){
    logger.info("process in TransactionProcessingService; amount=" + transaction.getAmount());
  }
}

Transactions over 10000$ processing service:

public class AntiMoneyLoundryService {
    private static Logger logger = Logger.getLogger(AntiMoneyLoundryService.class);
    
    public void process(Transaction transaction) {
        logger.info("process in AntiMoneyLoundryService; amount=" + transaction.getAmount());
    }
}

Spring configuration:

<beans:beans xmlns="http://www.springframework.org/schema/integration"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/integration
            http://www.springframework.org/schema/integration/spring-integration.xsd">
    <channel id="transactions" />
    <router input-channel="transactions"
            expression="payload.amount le 10000 ? 'normalProcessing' : 'antiMoneyLoundryProcessing'" />
    <service-activator input-channel="normalProcessing" ref="transactionProcessingService" />
    <service-activator input-channel="antiMoneyLoundryProcessing" ref="antiMoneyLoundryService" />
    <beans:bean id="transactionProcessingService"
                class="sample.TransactionProcessingService" />
    <beans:bean id="antiMoneyLoundryService"
                class="sample.AntiMoneyLoundryService" />
</beans:beans>

And finally code which sends two messages with two different transactions to the input channel:

ApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/creditCardTransactions.xml", TransactionProcessingDemo.class);

MessageChannel transactionsInputChanngel = context.getBean("transactions", MessageChannel.class);

Message<Transaction> normalTransaction = MessageBuilder.withPayload(new Transaction(new BigDecimal(9999))).build();

Message<Transaction> bigTransaction = MessageBuilder.withPayload(new Transaction(new BigDecimal(90000))).build();

transactionsInputChanngel.send(normalTransaction);

transactionsInputChanngel.send(bigTransaction);


Here is the log produced:

[TransactionProcessingService] process in TransactionProcessingService; amount=9999
[AntiMoneyLoundryService] process in AntiMoneyLoundryService; amount=90000

As we can notice, messages were properly forwarded by the router element to the appropriate services.

Java code should be self explanatory. Have a closer look at Spring configuration file.
In line 8 input channel is defined with id transactions. It defines a point to point message channel to which messages can be send and from which they can be fetched.

Line 9 introduces  router element. A Router determines the next channel a message should be sent based on the incoming message. Code in expression attribute is a SpEL expression to be evaluated at runtime. In the example presented the expression evaluates to a channel name. The other way to map the result to the channel name is using a mapping sub element.
The condition based on which channel is chosen can be also implemented in POJO.

Lines 11-12 contain service-activator definitions. A Service Activator is a component that invokes a service based on an incoming message and sends an outbound message based on the value returned by the service invocation.

Lines 13-16 contain simple beans definitions.

What do we get as a result of such design?

There is no coupling between TransactionProcessingService and AntiMoneyLoundryService.
Choosing the right service is fully handled by Spring Integration. The choose is based on condition placed in the configuration, not in the code itself.
These are the advantages of Spring Integration which we can see based on that simple example.

What is more, Spring Integration introduces and in some way enforces us to design our domain in an event-drive manner. If properly designed it perfectly mirrors the real world. We all live in event-driven environment. Each day we receive so many messages: mails, phone calls, text messages, RSS feeds, just to name a few. Software we produce, the business domain is like a slice of the real world. To increase success of the project the domain we model should reflect the real world the most accurately. Spring Integration is nearly a perfect tool to model that event-driven nature of any business we create software for.

Personally, I truly recommend deeper dive into that part of the Spring ecosystem. I am almost sure that no one will be disappointed.