The SharePoint developer community has produced a wealth of knowledge and code samples that demonstrate how to consume SharePoint web services and leverage them in domain-specific applications. This information is often task-centric, such as "How to add an Item to a List using SharePoint Web Services." In this article, we will take a framework-centric approach and apply sound, fundamental object-oriented (OO) design principles to implement a reusable library for working with the SharePoint web services.
Motivation and Goals
My motivation for this article is to help you create a foundation for a framework that can be used in one, or reused across many SharePoint-integrated applications. I also want to demonstrate how good software design principles can (and should) be applied to SharePoint development, and confess my own sins for not always following them. For some reason, I often find myself thinking procedurally when developing a SharePoint customization. Perhaps it's because the product is a 70% solution and requires a different mindset and approach than a large greenfield software project does. Maybe customers expect results faster when you build it on SharePoint, and I've taken shortcuts to meet them. Or maybe I've just been lazy, but in any case, here are some of the issues I've seen or been responsible for:
- Re-writing the same SharePoint code on each new project
- Not refactoring within and across applications
- Tightly coupling application code to the XML input and output from SharePoint services
- Lack of a domain model; designing procedurally instead of applying domain-driven design (DDD)
- Not having well-defined layers and strategies
- Failure to design for testability and not writing unit tests
Here are some of the goals I would like to achieve with this framework:
- Implement a domain model that is independent and decoupled from the SharePoint services
- Define a set of patterns and strategies for communicating with the SharePoint services
- Create a reusable library that can be used in many different domain-specific applications
- Use DDD and Test-Driven-Design (TDD)
- Apply good OO principals and patterns
- Use Windows Communication Foundation (WCF)
In the next section, I'll walk through the implementation and the process I went through to arrive at the design below. Generally speaking, I used DDD and TDD (where it made sense) and followed the Single Responsibility principle. If you are not familiar with this, it is often described as "a class should only have one reason to change." I call it the "and test." I describe the purpose of the class and if I have to use the word and, I consider refactoring some of the functionality to another class.
In the diagram below, the client application uses the repository and domain classes. The repository is responsible for retrieving domain objects based on criteria. The repository uses one or more service gateways to get the information it needs. The service gateway communicates with the SharePoint web services using a WCF client. The service calls typically return XML responses, and it is the job of the service response mapper to translate the XML to a domain object. Finally, the WCF client factory creates clients configured to call the SharePoint services.
In this example, the goal is to get all the lists in a given site. Following DDD, we will start the design in the domain layer and create a list class that has a title property:
Create the list repository class to define the method clients will call the get the lists in a given site:
Following TDD, we'll create a unit test that will fail until we implement the repository:
Return to the repository class and add the implementation to call the lists service gateway which we haven't created yet. How did I know to create a lists service gateway? I decided to have a gateway that corresponds to each SharePoint web service and did some research to find the exact service and method that would return the lists for a given site. The implementation below is very simple, but you can imagine a more complex repository that uses several service gateways and does some additional processing to construct a domain object graph.
Create the lists service gateway so the project will compile:
Create a failing unit test for the lists service gateway:
Next we'll add a reference to the SharePoint Lists service. Note: Visual Studio will add an app.config and a Properties > DataSources folder and you can delete both of them. We are going to configure WCF programmatically.
Return to the lists service gateway and fill in the implementation. Now I'm pulling a "Julia Childs" on you. I already went through several iterations of TDD and refactoring to arrive at the implementation below. Initially this method had all the code to call the web service, and there was no WCF client factory nor a mapper. After implementing this logic in several gateways, I refactored the commonality to classes. It made sense to have a factory to construct WCF clients in a consistent way and allow me to make a change in one place if I discovered something new. The Single Responsibility "and test" led me to refactor the mapping code to a separate class and the end result is a much cleaner implementation:
Next, we'll implement the WCF client factory. As you can see, it has been through a few iterations of TDD and refactoring starting with the service url class. This class contains constants and methods for working with SharePoint service urls. The other thing you'll notice is the the WCF configuration that is typically done in a configuration file is being done programmatically in the service binding factory class.
Here is the implementation of the service binding factory:
The gateway uses a mapper to map the XML response from the SharePoint web service to a domain object. The XML response is in the form of <element attribute1...n /> which, in this case, translates nicely to domain objects and properties. The code below uses Linq to find all List elements and for each one it calls MapInternal which creates a list object from the element:
Now if we return to the repository GetLists unit test, it passes and output each list title in the site:
In this article and walkthrough, I demonstrated how DDD, TDD, patterns and good OO principles can be applied to implement a framework that consumes SharePoint web services. Even though I focused on the web services, the SharePoint API can also be regarded as a service and the same fundamentals can be applied to developing applications that run on the server. I hope this inspires and helps you to implement your own framework and improve the quality and reuse of your SharePoint code.