Tuesday, June 22, 2004
Web Services exposing internal object types
Lately I have been looking at creating some WSDL for a few web services for the product that I am working on.
One of the issues that I am having is how to maximize the flexibility of the system by making sure that each layer within the web service hierarchy (web service stub/business/data layer) is independent of one another. One of the decisions that I have to make is whether or not to return a domain model object type via a web service method. For instance, suppose that I have the following domain model object:
Case A:
My business layer has a method that retrieves an object of type Product:
My web service calls the retrieveProduct method:
In this example, the WSDL is driven by the domain model (what I call schema last). There
are a few drawbacks to this approach:
One solution would be to add a mapper class that would translate between the classes exposed via the public web service (WSDL) and the classes used internally by the business layer. One way to do this is to define the WSDL independently of any application domain model classes.
Case B:
Suppose that I defined the WSDL for a product called WSProduct:
And change the web method to use this class:
Now the web service method returns a class of type WSProduct, and it obtains
the object from the ProductBusiness class via the retrieveWSProduct() method.
The retrieveWSProduct method on the ProductBusiness class looks like this:
The method creates a Product and a WSProduct class, and then it translates(copies)
the values from the Product class to the WSProduct class. It also sets the dateRetrieved field on the WSProduct class.
In this case, any changes to the Product domain model class will not affect the contract
exposed by the web service, since this contract is represented by the WSProduct definition
in the WSDL. Any changes to the WSProduct in the WSDL will only affect the ProductBusiness
object (retrieveProduct will need modification). The Product object will remain unchanged since it is only used internally and it is not exposed by the web service.
Now for the question:
One of the issues that I am having is how to maximize the flexibility of the system by making sure that each layer within the web service hierarchy (web service stub/business/data layer) is independent of one another. One of the decisions that I have to make is whether or not to return a domain model object type via a web service method. For instance, suppose that I have the following domain model object:
Case A:
public class Product
{
public string name;
public string sku;
public double price;
public Product() {}
public Product(string name, string sku, double price)
{
this.name = name;
this.sku = sku;
this.price = price;
}
}
My business layer has a method that retrieves an object of type Product:
public class ProductBusiness
{
public Product retrieveProduct()
{
return new Product("JellyBelly (8 oz.)", "PK09834", 2.50);
}
}
My web service calls the retrieveProduct method:
[WebMethod]
public Product getProduct()
{
ProductBusiness bus = new ProductBusiness();
Product prod = bus.retrieveProduct();
return prod;
}
In this example, the WSDL is driven by the domain model (what I call schema last). There
are a few drawbacks to this approach:
- The Product data type in the domain model is tightly coupled to the datatype in the public web service interface.
- If a public field is added to the Product domain model object, this public field will
be exposed via the WSDL. - If another class in the business model would like to access a public field on the Product
domain class, but we do not want to expose this public field via the web service, we would have to declare the field as non-public and expose the field to the client class via a helper class (or a helper method on the domain model class).
One solution would be to add a mapper class that would translate between the classes exposed via the public web service (WSDL) and the classes used internally by the business layer. One way to do this is to define the WSDL independently of any application domain model classes.
Case B:
Suppose that I defined the WSDL for a product called WSProduct:
public class WSProduct
{
public string name;
public string sku;
public double price;
public DateTime dateRetrieved;
}
And change the web method to use this class:
[WebMethod]
public WSProduct getWSProduct()
{
ProductBusiness bus = new ProductBusiness();
WSProduct prod = bus.retrieveWSProduct();
return prod;
}
Now the web service method returns a class of type WSProduct, and it obtains
the object from the ProductBusiness class via the retrieveWSProduct() method.
The retrieveWSProduct method on the ProductBusiness class looks like this:
public WSProduct retrieveWSProduct()
{
Product prod = new Product("JellyBelly (8 oz.)", "PK09834", 2.50);
WSProduct wsProd = new WSProduct();
wsProd.name = prod.name;
wsProd.sku = prod.sku;
wsProd.price = prod.price;
wsProd.dateRetrieved = DateTime.Now;
return wsProd;
}
The method creates a Product and a WSProduct class, and then it translates(copies)
the values from the Product class to the WSProduct class. It also sets the dateRetrieved field on the WSProduct class.
In this case, any changes to the Product domain model class will not affect the contract
exposed by the web service, since this contract is represented by the WSProduct definition
in the WSDL. Any changes to the WSProduct in the WSDL will only affect the ProductBusiness
object (retrieveProduct will need modification). The Product object will remain unchanged since it is only used internally and it is not exposed by the web service.
Now for the question:
- Which method is preferred? I would think that the case B is preferred over case A since there is less coupling
between the web service interface and the underlying implementation.