javaweb-servicesdesign-patternssoaploose-coupling

Reduce coupling in a SOAP client


Currently we're usign jax-ws to create and consume SOAP web services, generating java classes through the wsimport goal.

We then, create a Service class to call this web service. The code in the Service class is like this:

public WebServiceRS webServiceCall() {
  
    Security security = SecurityFactory.getTokenSecurity();
    MessageHeader header = MessageHeaderFactory.getMessageHeader(SERVICE_ACTION);

    LOGGER.info("Calling CreatePassengerNameRecordRQ webService ...");

    Holder<Security> securityHolder = new Holder<>(security);
    Holder<MessageHeader> headerHolder = new Holder<>(header);

    addHandlers(port);

    WebServiceRS webServiceRS = port.webServiceRQ(
            headerHolder,
            securityHolder,
            getRequestBody()
    );

    ...

    return webServiceRS 
}

So what we noticed and that could be problematic is that this Service class depends on the .wsdl generated files like Security, MessageHeader, WebServiceRS and so on. And so when we update the .wsdl file to a newer version the code would eventually break because of this dependency.

What we're trying to achieve, then, is a way to invert this dependency so that we can update the web service (i.e .wsdl generated classes) without changing our Service class. We're having problems because we haven't figured a pattern to solve this issue. The main difficulty seems to arise from the fact that we can't change these generated classe to implement an interface as an example.

We're interested in patterns or best practices that can loose this coupling.


Solution

  • Solution 1. Use Adapter pattern

    In your case, the contract of service will change and you need to sync two different objects. Adapter will provide a different interface to its subject.

    1. Create an interface for your client model
    public interface ClientModel {
        String getValue();
    }
    
    1. Create Adapter which will convert DTO to ClientModel
    public class ClientModelAdapter implements ClientModel {
        private DTO dto;
        
        public ClientModelAdapter(DTO dto) {
            this.dto = dto;
        }
    
        @Override
        public String getValue() {
            return dto.getValue();
        }
    }
    
    public class DTO {
        private String value;
    
        public String getValue() {
            return value;
        }
    }
    

    What is the exact difference between Adapter and Proxy patterns?

    Solution 2. Use Mapper Pattern

    You can simply create a mapper between DTO and ClientModel

    public class ClientModelMapper {
        ClientModel map(DTO dto) {
            ClientModel clientModel = new ClientModel();        
            clientModel.setValue(dto.getValue());
            return clientModel;        
        }
    }
    

    Mapper component that transfers the data, making sure that both DTO and client model don't need to know about each other.
    In this case, you can use MapStruct framework to avoid handmade mapping.

    Solution 3. Make service API backward compatible

    Probably you can avoid breaking changes in SOAP service and make API backward compatible. Backwards compatibility and Web Services