How a software service code looks like? A design snapshot

What is service-oriented design?

Service-orientation could be a fuzzy concept with many meanings; here is my understanding of service-orientation in a nutshell from a programmer perspective with a code sample.

Designing with service-orientation style is design at a higher granularity level, at the next level of granularity beyond individual procedures and functions, modules, objects and classes, and beyond components and individual applications. Some notes about granularity levels can be found in the following post:

Does SOA imply that OO is dead?
https://blogs.msdn.com/marcod/archive/2004/06/07/150420.aspx

The unit of composition at this level is the service, you know, like many commodities in your home like water, electricity, telephone, cable TV, pizza, etc., the service provider potentially distributes the good or service to many, many, many customers on a pay-per-use or single-pay contract including its service-level agreements that the consumer knows and operation-level agreements known only by service internals.

A software service is a full-fledged enterprise application with all its corresponding architectural properties like correctness, robustness, scalability, reliability, security and other targeted design goals for its external quality factors that satisfy an explicit contract surrounded with its service-level agreements; in addition that software service can be programmatically callable from other software service or applications in a composition relationship.

An important aspect of this relationship of composition is the very low coupling between the parts in it, in other words, there should be no technical restrictions to replace any part in the consumer/provider relationship.

So far, nothing implies the use of XML Web services except for this last low coupling property which could not widely be spread using other communications protocols and formats but with ubiquitous protocols like XML and HTTP. This implies that if a given context does not need such omnipresence, then those other mechanisms (i.e. DCOM, .NET Remoting, etc.) can do the job just fine.

There are plenty of resources with more details about service orientation, for example:

A Guide to Developing and Running Connected Systems with Indigo
https://msdn.microsoft.com/msdnmag/issues/04/01/Indigo/default.aspx

A software service

Arguably, a very good life cycle for a software service starts humbly, perhaps a very successful business application which gradually exposes its functionality as components for other applications or subsystems to use, as more consumers arrive the humble application continue is lifetime as a software service now with numerous consumers.

This pattern somehow follows the systems theory principle of why the systems work or fail by John Gall that reads like this: “a complex system that works is invariably found to have evolved from a simple system that worked”.

Designing software services from scratch could be a very risky approach, and as with kids, many people will just do exactly that.

For the sake of brevity, let’s assume the pre-existence of a successful software application that serve as a repository of diverse business entities as a hierarchical folder system with legal documentation associated to business processes: an electronic filing cabinet that a particular area has been using successfully.

Now, the company wants to widely spread this application as a software service providing current and future content to most areas in the company.

Software services interact via sending messages, conceptually those messages are envelops with documents inside. The message envelop has attributes about the message and content itself whereas the documents are relevant to the ultimate receiver. The flowing messages can be called Requests and Responses at a particular application protocol level, for the sample software service of this narrative the requests could be like:

“get a summary list of cases related to customer X”
“get the folder structure up to depth-level N for content of case Y”
“submit document D to folder F in content structure of case Y”
“add summary document D to business action S”

Here are features similar to a document management system with a hierarchical structure, kind of a file system but with much more metadata related to individual elements and containers; Microsoft Windows Server 2003/Windows SharePoint Services could be part of the design of this software service which will become a composite service aggregating another service in order to fulfill its own contract.

Let’s focus on the “add summary document D to business action S” request; the request message structure could be like this:

 AddSummaryDocumentToAction
    ActionID : Text
 Status : Enumeration
    Priority : Enumeration
  DocumentName : Text
 Document : Binary

Service-oriented level of design makes functional decomposition relevant again in contrast with popular object-oriented decomposition at other levels of design. Our request “add summary document to business action” is conceptually a procedure, a function, a task, a unit or work, a functor object, hence it resembles a traditional transaction of online transaction processing systems [3] and all their good rules and principles apply especially well in service orientation. This is a reason why being well versed with multi-paradigm design is important [4, 5].

A service request is ultimately processed by an archetypical kind of object: a processor.

A processor is the representation that captures all that conceptual mechanisms mentioned above, in essence: an operation—more in particular—a business operation. Hence, a processor is usually related to a transaction which the processor starts by itself or participate in the context of an existing one. Also, here is another aspect of the granularity level of a service operation where its scope matches a significant operation from the business process perspective.

An initial stage of a software service design could be a hierarchical functional decomposition like Figure 1 and it would be all right no matter how furious some object-oriented dogmatists can get. A key aspect of good design is simplicity and while a particular design doesn’t need the object-oriented machinery, it could be just fine without it (another point in favor of the practice of multi-paradigm design techniques).


Figure 1. Software service internal view of processor objects organization

Each blue circle in Figure 1 is a processor, composing or coordinating other processors or other data objects in order to fulfill the consumer request and return a proper response. Usually the client does not have direct dependencies over individual top-level processor objects nor from other levels but a client usually see just an application façade.

Along the same lines of software service design, keep in mind that its boundaries are explicit, that is, the difference between a local invocation and a remote invocation are clearly stated in the design; the pattern of invocation to a software service should follow a functional decomposition style at a coarse-grain level of capability granularity (in contrast with an fine-grain object decomposition style), usually mapping 1:1 to business or utility transactions.

While the hierarchy is small, more like a forest rather than a single big tree, it is no big deal keeping dependency management problems under control. That is, the big historical hindrance of functional decomposition is the direction of dependencies between operations: from the higher levels of abstraction to the lower-level details, where little changes on lower levels propagate all over the entire system structure above those levels, rippling the effects of brittle software. Here is where the object-oriented decomposition machinery comes handy with its dependency management principles and techniques so that change can be isolated into components of the software service.

Returning to the design of our service request, a simple functional decomposition (stepwise refinement) for it could be:

1. Add summary document D to business action S
   1.1 Commit properties of the business action S (status and priority)
   1.2 Commit document content to the repository associated with action S

Let’s designate a processor object for step 1 which will compose two other processor objects for steps 1.1 and 1.2.

Step 1.1 stores the request properties status and priority to an existing action with the ActionID qualifier; this business entity can be stored on a SQL Server table which the corresponding processor object knows about or, this entity can also be stored in a SharePoint List structure which conceptually is similar to a relational database table (see Figure 2).

Step 1.2 stores the document bits in relation to an existing action with the ActionID qualifier; the document could also be store on either a SQL Server table or a SharePoint List.


Figure 2. SharePoint Services: Server and Site Architecture

The code to access SQL Server is more well-known in comparison with SharePoint Services code, so I will show the SharePoint code.

Whereas the code to access SharePoint Services could be part of an ASP.NET application hosted in the SharePoint context and thus accessing the local SharePoint object model, the point of this narrative is to describe a basic sample of interaction of software services so invoking the SharePoint Web service API make sense to my illustrative purposes; beside the fact that many software services might not afford to be installed on the same server computer where SharePoint is installed.

The client code sends the software service request like this:

 ActionOperation op=new ActionOperation();
op.AddSummaryDocumentToAction(actionID,status,priority,docname,docbytes);

The essence of the server side code looks like this:

 class ActionOperation
{
  public void AddSummaryDocumentToAction
  (
    string actionID,
    string status,
    string priority,
    string docname,
    byte[] docbytes
  )
  {
    TransactionalContext tx=null;
    try
    {
      tx=TransactionalContext.Create(System.Data.IsolationLevel.ReadCommitted);
      AddSummaryDocumentToAction(tx,actionID,status,priority,docname,docbytes);
      tx.Transaction.Commit();
    }
    catch(Exception)
    {
      if( tx.Transaction != null )
        tx.Transaction.Rollback();
      throw;
    }
    finally
    {
      if( tx.Transaction.Connection != null )
        tx.Transaction.Connection.Close();
    }
  }

  public void AddSummaryDocumentToAction
  (
    TransactionalContext tx,
    string actionID,
    string status,
    string priority,
    string docname,
    byte[] docbytes
  )
  {
    PropertyOperation p=new PropertyOperation();
    p.CommitPropertiesToAction(tx,actionID,status,priority);

    DocumentOperation d=new DocumentOperation();
    d.CommitDocumentToAction(tx,actionID,docname,docbytes);
  }
}

Note the pair of methods with the same name that relate to the same operation, the first one is intended to used by the client who sends the service request and know nothing about transactional contexts because it just wants its request to be fulfilled. When this method is invoked, the processor object knows that it is the root object in a transaction related to the service operation so it creates the transactional context in the first place, the same that will be propagated through all composites processor objects that are going to participated in the local transaction created by this coordinating root processor object.

The second method is intended to be called by composer processor objects that want to propagate their transactional context into this processor object that, when this second method is invoked, knows it is participating in an existing transactional context created by other processor objects or by itself in the first method.
Also, this second method can be called from unit testing code which controls the transactional context for a test case, perhaps rolling back the transaction leaving the database in the same state before the unit test case.

As the last part of this narrative, I show the code for one of the aggregated processor objects, the one that invokes the SharePoint Web service:

 class DocumentOperation
{
  public void CommitDocumentToAction
  (
    string actionID,
    string docname,
    byte[] docbytes
  )
  {
    TransactionalContext tx=null;
    try
    {
      tx=TransactionalContext.Create(System.Data.IsolationLevel.Serializable);
      CommitDocumentToAction(tx,actionID,docname,docbytes);
      tx.Transaction.Commit();
    }
    catch(Exception)
    {
      if( tx.Transaction != null )
        tx.Transaction.Rollback();
      throw;
    }
    finally
    {
      if( tx.Transaction.Connection != null )
        tx.Transaction.Connection.Close();
    }
  }

  public void CommitDocumentToAction
  (
    TransactionalContext tx,
    string actionID,
    string docname,
    byte[] docbytes
  )
  {
    SharePoint.Lists lists=new SharePoint.Lists();
    lists.Credentials=System.Net.CredentialCache.DefaultCredentials;
    lists.AddAttachment("Tasks",actionID,docname,docbytes);
  }
}

References

[1] Does SOA imply that OO is dead?
https://blogs.msdn.com/marcod/archive/2004/06/07/150420.aspx

[2] A Guide to Developing and Running Connected Systems with Indigo
https://msdn.microsoft.com/msdnmag/issues/04/01/Indigo/default.aspx

[3] Principles of transaction processing by Philip A. Bernstein, Eric Newcomer

[4] Design paradigms marginalization
https://blogs.msdn.com/marcod/archive/2004/03/07/85379.aspx

[5] Abstraction stacks and multi-paradigm software design
https://blogs.msdn.com/marcod/archive/2004/02/19/76637.aspx