2

Please bear with me, if this type of question has been asked before. I am having a dilemma with object design and seeking experts' help here to come up with the correct/better design for a webservice client. I am attaching a crude UML diagram below:

I have been writing/fixing Java code, but just getting into object design/architecture. I am writing a webservice client in Java to integrate a commercial Address Validation Geocoding Webservice into our application. Ours is a Tax software written in PowerBuilder running inside EAServer. I am writing the web service client in Java, so it can be integrated with the PB client through CORBA.

I have done a similar service earlier to get some GIS information and this was slightly simpler design handling only XML for both Request and Response. I handled getting/sending request and response in the controller itself.

I started using the same design and hit a road block: current web service handles both XML and REST (through HTTP GET URL); actually, there are 2 webservice urls, the one for Address Validation/Geocoding supports XML or REST, and the other for searching addresses uses REST only. I want to design a generic request abstract class/interface and the controller that can be used for both the services. While trying to come up with the classes, I ran into a classic design issue. Now with Request itself can be a XML string or a URI object (for REST Request), my approach of getting the request and sending it in Controller seems flawed.

Should I make the Request object itself build and send XML/REST itself to the external Service or should I somehow manage to get different types of Requests into the Controller and send it from there (using getRequest, sendRequest)?

Also, I am learning to use Design Patterns in Java. What type of design pattern would be applicable here? I saw command pattern suggested for a similar client. Also, can MVC pattern be applied here?

I just want to get it right, before I started coding.

Web Service client for a Geocoder service

UPDATE:

My Address Record to send as part of request is,

public class AddressRecord {
  protected String AddressLine1;
  protected String AddressLine2;
  protected String City;
  protected String State;
  protected String PostalCode;
  protected String Country;
...
}

And my request object is,

public class Request {
  protected AddressRecord  records[];
  protected ArrayList      actions;
  protected ArrayList      columns;
  protected Properties     options;
  protected String         transmissionReference;
  protected String         customerId;
....

}

It can have one or addresses (I am planning to send only one for now). It also allows to specify the exact columns needed, actions to be done, Options etc.

And my XMLRequest extends,

public class RequestXml extends Request {
..
public String buildRequest()
{
...
}

And my problem came when I try to do REST Request similarly,

public class RequestREST extends Request {
..
public URI buildRequest()
{
...
}

Depending on the columns chosen, the address will be validated and the service will optionally pass back the Lat/Long for the address, if found. A typical Address Request and Response may contain one or more of the following record (in XML format):

Request record

<Request>
  <Actions>Check</Actions>
  <Columns />
  <CustomerID>1234567890</CustomerID>
  <Options />
  <Records>
    <RequestRecord>
      <AddressLine1>200 Main St</AddressLine1>
      <AddressLine2 />
      <City>Los Angeles</City>
      <PostalCode>90012</PostalCode>
      <State>CA</State>
      <Country>USA</Country>
      <CompanyName></CompanyName>
      <EmailAddress />
      <FirstName />
      <FreeForm />
      <FullName></FullName>
      <LastLine />
      <LastName />
      <PhoneNumber></PhoneNumber>
      <RecordID>1</RecordID>
    </RequestRecord>
    <TransmissionReference />
  </Records>
</Request>

Response record: Will contain validated and standard formatted address record.

<ResponseRecord>
  <AddressLine1>200 Main St</AddressLine1>
  <AddressLine2></AddressLine2>
  <City>Los Angeles</City>
  <State>CA</State>
  <CompanyName></CompanyName>
  <EmailAddress></EmailAddress>
  <NameFull></NameFull>
  <PhoneNumber></PhoneNumber>
  <PostalCode>90012</PostalCode>
  <RecordExtras></RecordExtras>
  <RecordID>1</RecordID>
  <Reserved></Reserved>
  <Results>AS01</Results>
  <AddressExtras></AddressExtras>
  <AddressKey>92688211282</AddressKey>
</ResponseRecord>

I am probably stretching it too much. Like @Vladislav suggested, I could probably make buildRequest in URI request also return a String, and that might solve this issue; that can get this job done, but I am trying to learn to do it the proper way of identifying objects and design.

UPDATE2: @Vladislav, This is what I had for an earlier Webservice client. They called their service GeoQuery, so I named it BoeQueryManager. Does this qualify to be the transport class you are referring to? I think this qualifies to be the Controller. The GeoQuery_MyLAService, generated by WSDL2Java may be the Transport object you referred to? I have redone the diagram, for the new program, to reflect the changes I see per my new understanding of your comments. Can you please validate that?

FYI, our address validation is done in 3-parts - Address Validation/Search, Geocode and once we get the Lat/Long, do the internal GIS function to get city layers. I already did the 3rd part, now working on replacing the 1st and 2nd services.

public class BoeGeoQueryManager {


  private void init(String aUrlStr) {

    try {
      URL endPoint = new URL(aUrlStr);
      GeoQuery_MyLAService service = new GeoQuery_MyLAServiceLocator();
      geoq = service.getGeoQuery_MyLAcfc(endPoint);
    } catch (MalformedURLException me) {
      logger.error("BOEGIS", "", this.toString() + " - " +  "init", "Error initializing. " + me.getMessage());
      me.printStackTrace();
    } catch (ServiceException se) {
      logger.error("BOEGIS", "", this.toString() + " - " +  "init", "Error initializing Service. " + se.getMessage());
      se.printStackTrace();
    }
  }

  // Search by Address
  public String queryBoeGis(String aAddress) throws Exception {
    BoeXMLRequest lRequest = new BoeXMLRequest(aAddress, 0, 0, GISRequest.SEARCH_MODE_ADDRESS);

    return queryBoeGis(lRequest);

  }

  // Search by Lat/Long
  public String queryBoeGis(double aLatitude, double aLongitude) throws Exception {
    BoeXMLRequest lRequest =
        new BoeXMLRequest(GISRequest.REQUEST_BLANK_ADDRESS, aLatitude, aLongitude,
            GISRequest.SEARCH_MODE_LATLONG);

    return queryBoeGis(lRequest);

  }

  // 3. The controller is doing the Webservice call here. I just pass in 
  // the Request object into this. Controller is fully aware it's a XML
  // Request/Response.
  public String queryBoeGis(BoeXMLRequest aRequest) throws Exception {

    String respXML = "";
    String reqXML = "";

    requestCount++;
    try {
      reqXML = aRequest.getXml();

      if (aRequest.isModeLatLong()) {
        respXML = geoq.qByCoords(reqXML);
      } else if (aRequest.isModeAddress()) {
        // search by address (or both)
        respXML = geoq.addressValidationService(reqXML);
      }
      respXML = XMLDocument.cleanup(respXML);
      BoeXMLResponse boeResponse = new BoeXMLResponse(respXML);

      // To be able to get a flattened XML to be sent to the Legacy application
      if (adjustXML)
        respXML = XMLExtractor.getAdjXml(aRequest, boeResponse);

    } catch (Exception e) {
      e.printStackTrace();
      respXML = "";
    }

    // Otherwise, return the original XML
    return respXML;
  }
}

Refactored design

  • SamV -- You are asking really good questions here -- but you are essentially doing a design review. Stack exchanges work best when you ask specific questions, one at a time. Please read this discussion on how to ask questions. – Jay Elston Nov 20 '15 at 2:14
  • @Jay, thanks. This was my first question on this site; it actually started on Code Review site and per someone's suggestion there, I posted here. I admit having gotten carried away with the nice design session. I will keep your suggestion in mind for the next question. – SamV Nov 20 '15 at 18:29
2

You created two parallel class hierarchies: Request with concretes (XmlRequest, JsonRequest etc) and Response with concretes (XmlResponse, JsonResponse etc).

But what I think you have to deal with is a Request / Response as a business entities (Request can be as simple as a string, containing address to geocode for our example, and a Response - an array of two floats - geographic coordinates) and a number of transports (Json, Xml etc.).

Each transport has it's Request and Response encoder / decoder of the original Request object to represent request and response from appropriate web services.

This way you will decouple your business logic from concrete implementations.

Your controller will create a Request object and then, depending on some conditions, choose appropriate transport for it to send / receive data. Transports serialize Request, communicate with external web services, receive and deserialize Response and return it to the controller.

UPDATE:

After seeing your updates above I would like you to check if you need Request class descendants at all. Your request / response scheme is highly customised depending on the transport. Why not to have a single Transport class hierarchy, which will receive your Request object and return Response?

Transport will handle all URIs or whatever internal protocol details are.

UPDATE 2:

just curious: while learning OO, I keep hearing about making objects self contained. This is why I made XMLRequest etc, thinking that's the only one that knows about the request.

Well, that's right. It's about SRP actually. If an implementation is very simple, you can assign 'transport' responsibility to a Transport class. If implementation becomes or is planning to become complex, you transform Transport into a module, make facade for it and then assign 'request handler' and 'response handler' responsibilities to classes, inside this module.

When I'm doing OO design, I'm actually trying to keep system understandable and separable at all "zoom" levels: layer -> module -> class -> class member.

I guess your second diagram is much better, but still blurs responsibilities between classes. Controller should create Request object and use some kind of factory to instantiate Transport. In this way, transport details are hidden from controller. It should know about abstract Transport class / module without going into details about its implementation.

  • thanks for taking the time to answer my question and suggest alternate design. It makes sense, come to think of it, my request/response objects probably try to be the transport objects you are referring to. Webservice actually gets address validation + lat long, so I need to pass in parts of the address. I've updated my question. Can you please let me know, if it's doable with extra details? – SamV Nov 17 '15 at 22:59
  • 1
    @SamV I've updated an answer – Vladislav Rastrusny Nov 18 '15 at 7:34
  • just curious: while learning OO, I keep hearing about making objects self contained. This is why I made XMLRequest etc, thinking that's the only one that knows about the request. I see your point about Transport object being the only one that knows about the protocols, req/response formats etc and the request/response object are just purely that. If you don't mind me asking, I want to get in on how you arrived at that design. How would you put in object design terms? Did you apply any design pattern or does that knowledge just come with experience? Thanks. – SamV Nov 18 '15 at 22:08
  • thanks a lot again for taking the time and expert advice; after trying to code my own Update2 I did few hours ago, I now realize what you meant. Long procedural coding is clouding my object ability. Updated the diagram and my question in Update2 with my new understanding. Will you be kind enough to please comment or critique? I think I now see your controller, transport etc. This is probably what I did in my last program, only didn't realize it. (I used axis library to do WSDL then). With the new task doing HTTP (XML, JSON, REST), what you said feels more relevant. Thanks. – SamV Nov 18 '15 at 23:28
  • 1
    @SamV You are welcome. I've updated answer again with details. – Vladislav Rastrusny Nov 19 '15 at 13:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.