This design is intended to be used to reduce the role of Stateless Session EJBs or similar 'infrastructure' code. Instead of defining the business interface on the Session EJB, it is reduced to the context of providing just the roles that EJBs can be said to be good at; transaction scope and isolation, resource provision, and remote execution and distribution.
Developers writing business logic grapple purely with plain Java design and coding; they do not require to be J2EE (or other container type) experts. J2EE expertise is minimised to the core of developers working on the ServiceProcessor component.
Additional complexity of the container logic is not forced into the business logic. The service delivery is separated from the service logic. Business logic is isolated from the execution context, which is reduced to the role of 'plumbing' only. There is a clear separation of concern.
The 'business interface' which is typically implemented onto Session EJBs, is replaced with a delegate. The business methods are defined on the delegate which then use the infrastructure to execute a corresponding service. The EJB interface does not map one to one methods to the business interface defined on the ServiceDelegate.
In this implementation of the design, we have chosen to refer to use an EJB as the 'ServiceProcessor'. However the ServiceProcessor does not have to be an EJB - you can easily replace it with a plain java object of some kind (e.g. for testing purposes) or another type of container object. However other types of container may mitigate against the reasons for using this pattern, for example by being able to directly run business logic without the interference of specialised infrastructure interfaces such as javax.ejb.Session
When not to use this pattern
If you are using entity beans for persistence, I would not recommend this design. This pattern has so far been implemented only in systems using Hibernate for persistence.
If your business logic is very simple and you are committed to execution on EJB backend, you should stick to the simple Sun J2EE patterns or other suitable design.
Conversely, if you have complex interaction between business logic elements that requires fine grained control over the transaction scope, this design could produce extra undesired complexity. Services that have to call other services in this design should only do so via commonised plain java code that is accessible to both services, rather than introducing a circular dependency back to the service interfaces.
Basic elements
I will now describe the static structure of basic elements in this design.
Domain
There is one special requirement of the domain. Because of the service processor and service commands each implement a single consistent interface, all domain object input and output to the business logic typically must be encapsulated in a transfer object interface. The transfer objects are aggregations of domain objects for a single or set of like business processes, i.e. fairly standard DTOs. The client is not concerned with the DTOs directly, as the ServiceDelegate defines a simple typed interface to the remainder of the design.
In the diagram, that transfer objects are defined as Request and Response pair extending from a TransferObject interface. This is not a necessary feature; for some domains, this may be too complex or not desired for other reasons, and a simple TransferObject is all that is neccessary. Simply using java.io.Serializable could be an option for generic systems although possibly cumbersome in implementation code.
The domain also includes a ServiceContext object into which system meta-information can be placed.
ServiceDelegate
The ServiceDelegate contains the 'API' of the system that a client would use, such as a Business or System Analyst might define. Clients of the ServiceDelegate (e.g. Struts Actions) do not see the transfer objects or the ServiceProcessor or other artifacts of the system. The ServiceDelegate defines its methods as taking and returning primitives, domain objects, etc, as appropriate to the functional requirements of the system.
Under the hood in the ServiceDelegate implementation, the delegate assembles the requisite transfer objects, looks up the ServiceProcessor implementation, invokes the service process, performs error handling and unmarshalls the return values for passage back to the calling client.
The client obtains an instance of the ServiceDelegate via a ServiceDelegateFactory. The delegate factory can use any suitable design to obtain the instance of the ServiceDelegate via configuration or dependency injection etc. Therefore a suitable ServiceDelegate implementation can be swapped in for such differing needs as testing, LocalHome vs RemoteHome implementation and other needs. For example a web service interface to such a system, would wrap an appropriate ServiceDelegate and not the ServiceProcessor directly.
In a system with more than a trivial number of business methods, there would probably be a delegate per business or infrastructure domain, e.g. CustomerSvcDelegate, InventorySvcDelegate SecuritySvcDelegate, etc. However this design up to the user of it.
ServiceProcessor
The ServiceProcessor in the documented design is an Stateless Session EJB. It could however be a plain java class, or some other sort of infrastructure system class, like a Stateful Session EJB, or an MDB, a mock class for testing, or a class run inside of a Spring container or similar.
The ServiceProcessor has a single method execute(String cmdName, TransferRequest request) and returns a TransferResponse object (with the caveats set out in the Domain section above). The String parameter, cmdName is used to allow the ServiceProcessor to locate the correct ServiceCommand via the CommandLocator. However this could be dispensed with in a number of ways, for example the concrete class of the TransferRequest could indicate with ServiceCommand (where you have a one to one mapping of transfer request classes to ServiceCommand classes) or a parameter could be set within the request or the service context objects.
If you require different transactional
8
scope i
1ff8
n different operations, it is possible to have several different ServiceProcessors. There could be one with bean controlled transactions, one with TX_REQUIRES_NEW and another with TX_REQUIRED and so forth (alternatively there could be serveral methods on the one processor each defined with a different transaction attribute). The ServiceDelegate has the responsibility then to choose which ServiceProcessor is approriate for its circumstance.
ServiceCommands
The ServiceCommand package contains the business logic of the system. It would interface to a persistence mechanism of your choice, designed in a standard way.
The ServiceCommand instances are obtained by a ServiceLocator or factory class that takes the relevant service specifier information as outlined above in the ServiceProcessor section. A service mapping layer is used to take the service discrimination (eg String, Class or Domain based) information and map to the required ServiceCommand implementations. ServiceCommand instances could be obtained from an object pool or constructed anew each time depending on requirements.
ServiceCommands use standard DAO pattern or other suitable persistence and communications encapsulation. They decompose the DTO input and perform the required business actions upon the domain information therein.
UML Sequence
This first UML Sequence shows a simplified set of operations performed when the client uses the ServiceDelegateFactory to obtain an instance of a ServiceDelegate and call a business operation on the delegate. Details such as the obtaining the EJB RemoteHome and remote interface are ommitted for clarity.
The next UML Sequence shows a simplified set of operations performed when the delegate calls the ServiceProcessor in response to a client's business operation call. It shows the processor using the discriminator via the configuration system to obtain an instance of the correct ServiceCommand. Then the ServiceCommand interface's execute method is called to perform the required business operations. Finally the response DTO is returned to the delegate, which unmarshalls it and returns domain data to the client.