Service interface layer

In this article I would like to describe a little about my thoughts on how to define API of service. I will explain how to define arguments of request and result of response, which could make API more stable and robust. This approach could be applied in any transport protocol or any programming language as well.
The very straight forward and the first version of your service’s API could look like:

public interface ICustomerService
{
    void CreateCustomer(string firstName, string lastName, string email, string streetAndNumber, string city, string zip);
    ...
}

There is nothing bad with this API, but take in mind it is public API. Once you will release your service and start to maintain it, you will probably extend your Customer information. You will need to know phone numbers, persist addresses in all countries around the world. Maybe you will want to know birth date, or from which system she/he is registering. Maybe you will have different kind of customers, or roles and you will want to manage them within this single service. How time flies and how requirements are grown, the number of arguments are changing and your code is full of arguments.

public interface ICustomerService
{
    void CreateCustomer(string firstName, string lastName, string email,
                        Date birthDate, int systemId, bool adsAgreed,
                        string streetAndNumber1, string city1, string zip1,
                        string streetAndNumber2, string city2, string zip2,
                        List<Roles> roles);
    ...
}

There is way how to decrease number of arguments by implementing more data classes. There is also possibility how to deal with compatibility. In my last project I learnt very nice API definition, which makes the life a little easier.

public interface ICustomerService
{
    CreateCustomerResponse CreateCustomer(CreateCustomerRequest request);
    ...
}

public class CreateCustomerRequest : BaseRequest
{
    ...
}

public class BaseRequest
{
    public string ApiVersion { get; set; }
    ...
}

public class CreateCustomerResponse : BaseResponse
{
    ...
}

public class BaseResponse
{
    public bool Success { get; set; }
    public int ErrorCode { get; set; }
    public string ErrorMessage { get; set; }
    ...
}

You can see that method CreateCustomer has one argument, data class inherits from BaseRequest, and it returns object of data class inherits from BaseResponse. This method definition is final and it need not be changed ever in future. You can extend your arguments by changing class CreateCustomerRequest or you can just modify its properties.
If you will need to deal with forward or backward compatibility from arguments perspective, you can add property called ApiVersion into your BaseRequest class. In the implementation you can distinguish calls by checking this property first.
There is also way how to deal with compatibility from response perspective. Take in mind that all transport protocols serialize object in the way, that object can be deserialized on other side. This is usually done by , pairing. So deserialization will take pairs which have same property names as deserialized object and all others are thrown away. If there is missing some property, deserialization will set it to default value. So from that point there is possible to have one, last version, of Response object, which can be deserialized by all versions of clients.
Of course, the compatibility of service API is complex problem and it cannot be simplified. But by this technique you can support all versions of your client with one service. And the complexity could be moved to one centralized point into the service.

That’s all, now go write some code.

Leave a Reply

Your email address will not be published. Required fields are marked *