Single responsibility principle (SRP) states that “every context (class, function, variable, etc.) should have a single responsibility, and that responsibility should be entirely encapsulated by the context". If you consider a java application running on a single JVM (or simply “java process") as a context then SRP could be also applied for building large server-side applications as a set of cooperating java processes.
Enterprise Integration Patterns are the key to clearly define context for each java process as well as how to put all java processes together into a solid multi-process application. The simplest SRP-compliant java process could have the following components: Receiver, Processor and Sender:
Receiver’s primary responsibility is to know how to consume messages that other java processes or external systems can forward to it. Then receiver passes the message to Processor. Receiver incapsulates transport, protocol and message format details. For example receiver could be implemented as a JMS listener for durable topic as well as a java.util.Timer that is polling some FTP server.
Processor uses incoming message in order to produce some kind of useful output.
Sender knows how to deliver useful output produced by Processor to other java processes or external systems. Sender incapsulates transport, protocol and message format details. For example it can open a plain socket and send bytes over the network, update rows in database or send SOAP message.
Processor is the core part of SRP-compliant java process. It has to be the context with single responsibility for java process while Receiver and Sender are just the auxiliary components for communication between cooperating java processes and external systems.
Let me give you some typical examples of responsibilities that could be associated with single java process:
  • Transformer - transforms incoming message to another format
  • Mailer - sends emails with predefined templates
  • Facade, Adapter or Broker - incapsulates communication with external system or service
  • Calculator - treats incoming message as an argument to calculate some business value
  • Monitor - accumulates report messages per each business entity during its processing at different states of business flow
  • Static data provider - dictionary holder for data that is not supposed to change a lot like countries, languages, timezones, currencies etc…
  • Cache - auxiliary component for performance boost, i.e. by in-memory caching from database
  • Report generator - exports xls or csv file from database according to incoming parameters
  • Database Manager - API and single entry point to insert, delete or update business entities in database for all other java processes
In order to have a solid multi-process java application you orchestrate SRP-compliant java processes and external systems, i.e. like the following:
The benefits of going for multi-process java application are:
  • You can fully control classpath for each java process and avoid JAR hell and subtle Classloader issues.
  • You can tune JVM heap and non-heap memory as well as choose the best GC strategy for every single JVM.
  • You can configure logging level and rolling file size per each process.
  • Development of each component could be done in parallel by different developers.
  • Easy to test Processor by unit tests, easy to test Receiver and Sender by integration tests
  • Crash of single JVM does’t impact all flows (see pic above why).
  • This is scalable design without any significant change in business logic in order to scale.
  • You don’t have to release the whole application but just the components that were changed (see pic above why).
The drawbacks of choosing multi-process java application are:
  • You have to provide complex start-and-stop infrastructure (i.e. by platform dependent scripts) that correctly starts and stops all your java processes. Start and stop order of JVMs is a challenge as well as duplicate java process handling which may occur if start-and-stop scripts are executed in parallel.
  • Complex CI scenarios must be implemented on build machine in order to build, test and deploy multi-process java application to test environments and developer’s machine.
  • Jars and API version incompatibilities between the components are expected a lot, that’s why integration phase usually is not smooth and not painless.
  • Development process hardly depends on Message Broker reliability, network and other infrastructure stability: sufficient memory and disc space, availability of free ports, enough file descriptors per native process.
  • You must constantly monitor your JVMs in order to be aware of errors and exception in logs, crashes or performance degradation of your JVMs.