We’ve recently been having a lot of discussions about how we develop our services. We’re using WCF (Indigo) to develop our services so we get to let it handle a lot of the plumbing for our services. We develop our services by creating an interface that defines our service contract and then create a concrete implementation of our service by creating a class that implements our service interface. All well and good. When we get into the implementation of our services we start to feel all the uber-SOA dudes of the world give us bad looks. We’re currently doing the following things that most SOA fanatics would beat us over the head for:
1) We’re not applying the rule that I wrote about way back when. Web services should have a single parameter. This means that instead of something like this:
public class CustomerService {
public Customer FindCustomer(int customerId) { /// }
}
we should be wrapping our customerId up in a DataContract so that our services can be versioned as it evolves like so
[DataContract]
public class FindCustomerRequest {
[DataMember(Name = “CustomerId”, Order=1, IsRequired=True)]
private int customerId;
public int CustomerId {
get { return customerId;}
}
}
public class CustomerService {
public Customer FindCustomer(FindCustomerRequest request) { /// }
}
2) We’re passing our domain objects back and forth in our services. So our domain objects are being decorated with [DataContact] and [DataMember] attributes. Since 99% of the time our services need to return a message that looks exactly like our domain objects we’ve been doing just that and returning them as is across our service boundry. In order to separate our services implementation from our domain layer we would ideally have a separate DataContract defining our messages that can be transformed into our domain objects. So instead of…
[DataContract]
public class Customer {
[DataMember(Name=”Name”, Order=1, IsRequired=true)]
private string name;
///yada yada yada…
public SaveObjectResponse Save() { //…}
}
We should have our domain object with no DataContract/ DataMember, a separate DataContract used only by our service, and a translator class that converts between the two….
namespace MyCompany.DomainLayer {
public class Customer {
private string name;
public string Name {
get { return name;}
set { name = value;}
}
}
}
namespace MyCompany.Contracts {
[DataContract]
public class CustomerContract {
[DataMember(Name=”Name”, Order=1, IsRequired=true)]
private string name;
public string Name {
get { return name;}
set { name = value;}
}
}
}
namespace MyCompany.ServiceTranslators {
public class CustomerTranslator {
public DomainLayer.Customer CustomerFromContract(Contracts.CustomerContract customerContract) {
DomainLayer.Customer customer = new DomainLayer.Customer();
customer.Name = customerContract.Name;
return customer;
}
public Contracts.Customer ContractFromCustomer(DomainLayer.Customer customer) {
Contracts.Customer customerContract = new Contracts.Customer();
customerContract.Name = customer.Name;
return customerContract;
}
}
}
While the sample code for the sample above isn’t too bad, imagine how repetitive and boring this could get
None-the-less it does provide a clean separation between your service and domain layers which helps ensure that changes in our services don’t directly affect your domain layer as well as vice versa. With tools like the Web Services Software Factory the grunt work required for implementation your services using the above method can be reduced to a couple runs of their wizards. But as you know I don’t like wizards. Could there be a better way?