Monday, December 31, 2012

Some thoughts on service naming / service capability naming conventions (2/2)

This post was created due to a few questions on this topic in my initial post on service and service capability naming in 2009. Elena's questions revolved around concrete service naming (endpoint naming). I had promised Elena a month ago that I would come back to this topic once I could sort out my time so here goes....

Abstract approach first:

Service and service capability naming is driven by the context (purpose) of the service or capability. The service defines the context itself and the capabilities define what you can do within that context.

By this definition, no relation exists between the service or capability name and the transport, end point type, parameters passed etc.

This post conveys the message that regardless of the implementation, transport etc. the name of the service or capability is not affected.

The example in the previous post used, was the Order (entity) service. The 'Order' context defines the actual order service name: 'Order'-Service.

Within the OrderService, a number of example capabilities exist:
- create
- get (or getDetails depending on you naming conventions)
- getHeader
- update
- updateShippingAddress
- cancel (cancels a running order)
- search

This order service apparently has 6 capabilities, following the CRUD approach which is common for entity services. One more #7 capability exists and is used to find orders (Order::search). (Note that the :: notation was borrowed from C++.)

In an object oriented language like C++, method overloading can be used to have several versions of the same capability.

As an example, let's explore the search capability a bit more: the Order::search is defined as:
Description: Search for orders and return a list of orderIDs which adhere to the search criteria.
Input: various (combinations of) search criteria
Output: List of order IDs (list can have 0, one or more results)

Let's take a closer look at this contract:
- The context is always the same for all capabilities: Order.
- The purpose of the capability is searching for orders; this is always the same regardless of how I search (which parameters used).
- The result of the call is always the same: a list of orderIDs is returned

So if all of the above are the same, the only one factor that might change is the way I search for orders (or in technology speak: which parameters I use).

As an example, I can search for orders based on:
- address
- customer lastname/city/DoB
- customer ID.
This means I can search using one of three ways for finding search results.

In C++ this can be solved by method overloading which might allow me to define the following methods (please ignore the syntax I'm just trying to convey a message here):
- Order::search(geoAddress)
- Order::search(Lastname, City, DoB)
- Order::search(customerID)

As you can see in the example above, in C++, although different parameters are used in the method signature, the actual name of the operation does not change. The reason is because context, purpose and result are the same; we're just using different search criteria.

In Web services, the same applies: if context, purpose and result are the same, there is no need to change the service capability name. The concept of method overloading does not exist in web services. This means that, in order to achieve a capability name that does not change, another solution must be found.

A significant difference between C++ and Web services exists (between most OO languages and Web services in general):
- C++ uses parameters in the method signature
- a web service only has one input parameter: the input document 

An example that shows how a capability only has one input document:
<porttype name="OrderService">
  <operation name="search">
    <input message="ns0:searchCriteria" />
    <output message="ns0:orders">
  </output> </operation>
In practice we never talk about input/output parameters when we talk about web services. Instead, we talk about input and output documents. The input/output documents can be complex XML structures and this means that we can still have some flexibility in this area: we can define an input document that allows to take in complex XML search criteria.

Concrete approach:

Following the abstract approach, whenever an actual service is built, the service can be used in various scenarios and implementations.

One of Elena's questions was whether or not there would be one WSDL per service or per service capability? I'm personally fond of one WSDL per service and would only break up if there are size/readability issues or in case there is a security need to now show specific operations in certain scenarios. As a consequence, if you ask me the first generic implementation of a WSDL would be to have all capabilities listed as operations in the WSDL document. So one OrderService.wsdl and multiple operations in the same service. The reason why I'm so fond of this approach is because it reflects the context definition of the abstract approach so beautifully.

Now let's try and solve the method overloading challenge from the first part of this post. If we create an input document for the search capability that looks like this:
  <xs:complexType name="searchCriteriaType">
      <xs:element name="Address" minOccurs="0">
            <xs:element name="StreetName"/>
            <xs:element name="HouseNumber"/>
            <xs:element name="City"/>
            <xs:element name="Country"/>
      <xs:element name="NameAddressDOB" minOccurs="0">
             <xs:element name="CustomerLastName"/>
             <xs:element name="DateOfBirth"/>
             <xs:element name="City"/>
      <xs:element name="customerID" minOccurs="0"/>

we can use any of the three listed possible ways of searching. By adding more restrictions to the XML (not a fan!) you might for example indicate that only one of the three search criteria can be used in any given call).

Different service platform to implement a service

A service inventory can be comprised of hundreds or more services and since service-orientation allows for flexible choices regarding vendor platform etc. can allow for quite a heterogeneous environment. Sometimes by adding or removing new vendor technologies (remember this is like gardening; the service inventory is not a static thing; it grows in certain areas at certain times of the year, and shrinks in others) the implementation of certain functionality might be moved around. If the name of the service would depend on the vendor platform, this would mean that when changing the platform, the service had to be renames with all the cascading consequences (repository retire old service, create new service, fix dependencies etc). Generally speaking a bad idea. The better approach would be to leave the service and capability name the same and use a versioning naming scheme to solve this problem. The new implemented version would not have a major version increase if the service remains compatible (assumed here) and only a limited cascading effect would be noticed (regression testing mainly).

Conclusion: do not couple the name of a service or service capability to the platform or vendor stack on which the service is implemented.

Different language to implement a service

Similar to the previous one if, by coincidence, a particular programming language is used to implement a service, this should not influence the name of the service (capability). Sometimes specific tasks are solve easier in one language than in another for any number of reasons, and the context, purpose and result of the call does not change, so why rename it? Conversely, a service (capability) can be comprised of more than one component and not all of them need to be written in the same language (ie. combination of J# and C#). If the language used to implement a service really mattered, this would be a nice naming challenge...

Conclusion: do not couple the name of a service or service capability to the language or component technology used.

Different transport used on a service capability

Assume that we have a web service implemented using SOAP 1.2. Typically the message is sent on the HTTP transport, but for some capabilities (or for all) we can allow for a JMS based transport if needed. This means that the one service capability can have multiple transports. It does not make any sense at all to create  two capabilities just for this purpose. Modern middleware allows different end points on different transports almost seamlessly. It's the same code that is executed, it is just accessed in another way. This does not at all justify a different name. In the service repository, one might find two transport references with this service:  "HTTP, JMS" but that's all - the service capability is and remains the same.

Conclusion: do not couple the name of a service or service capability to the transport technology used.

Different interface implementation

Assume we have a web service implemented using SOAP. Additionally a REST interface is required. Does that mean I get a searchREST and a searchSOAP capability? The answer is a clear NO! Reason for this is again that it's not a different capability; it is just accessed in a different way. It is still the same context, purpose and result. When looking at how we access these interfaces, these are clearly distinctly different and can be easily identified from each other just by looking a the message contents; no need to create a different capability name for that.

Conclusion: do not couple the name of a service or service capability to the interface implementation used.

Different underlying database

Already addressed in one of the other topics I posted on service abstraction. Regardless of the underlying database implementation, the context, purpose and result of the service remains the same. If we applied principles like standardized service contract and service abstraction, the names of the message and the vocabulary used in the messages themselves we would have already abstracted from ANY implementation aspect, including abstraction from the database technology. If we would expose that this service was implemented on a specific database technology we would cause a number of issues
- we can let a consumer 'assume' it is always this technology - basically not giving us the freedom to evolve the service onto another RDBMS product
- we can create a security risk where malicious consumers can target the service is such a way to abuse any weaknesses in the underlying database to get access to data unintentionally, or worse, change the data in ways we don't like etc.

Conclusion: do not couple the name of a service or service capability to the underlying implementation or RDBMS used.

Different versions of a service capability

Now this one definitely does allow for different service (capability) naming. Just a thing or two that you can pick up from the Web Services Contracts Design and Versioning book by Thomas Erl et al: Make sure that
- a service is versioned
- a service capability is versioned
- compatible changes of a service do not break the contract
- incompatible changes must break the contract - to facilitate this an incompatible version change of a service is accompanied by a service name change and/or a namespace change (to force consumers to change their implementation as well)

So initial versions of the OrderService may be called OrderV1. Minor changes would not be reflected in the name; major changes would be reflected in the name. So only an incompatible change can result in OrderV2 service.

I realize that perhaps this is a bit of cutting corners but it would probably best be covered in another post (working on that post early 2013) as one paragraph would not do the topic any justice.

Conclusion: do not couple the name of a service or service capability to compatible changes to a service.

Conclusion: do couple the name of a service or service capability to incompatible changes to a service.

How can service naming improve discoverability?

Having the same name for a service capability allows us to better discover a capability. If I would try and find the search capability for Order I would find only one I hope:
- When looking a the details of that capability I would find that (coincidentally) the service was implemented as a web service, is executed in a specific context (order) had one purpose (search for specific orders), had one type of result (list of order IDs), had a specific version (ie. 2.1), supports REST and SOAP; supports specific transports (HTTP 1.1 and JMS 1.0), and what the input and output documents are (more is available perhaps).
- I would (depending on access authorization) not be able to see whether it was implemented on the MS/Oracle/IBM or Tibco platform, I would not be able to see what the versions of these platforms used are, and I would not know what the underlying database technology was.

If I would follow the other approach an have a different name for every scenario of a capability, this would also mean that I would have to document equally many service capability profiles etc. Finding a specific capability would also be more complex as I would have to know more about the implementation of a service up-front; Ie. if I would not know up-front which different versions of which attribute (platform, transport) etc. exist, I would not know how to find the capability. To me at least this does not make any sense.

I hope that this post contributes to a healthy understanding of the concerns of service naming and service capability naming. Be aware that this is my view of the topic, and I do realize that probably for each argument why you should or should not change a service (capability) name, a dozen arguments exist to do the opposite. Also I recognize that exceptions may exist which justify a different name where I am arguing for not changing it, I would however treat them as exceptions, not as standard behavior!

- Roger

No comments:

Post a Comment