Bringing together repository and tree structure in business layer

06 Dec 2012

This post is part of series:

Part 4 – Strongly typed SharePoint list operations using the repository pattern in F#
Part 3 – Bringing together repository and tree structure in business layer
Part 2 – Creating and working with a custom tree data structure in C#
Part 1 – Strongly typed SharePoint list operations using the repository pattern

Now that we’ve created the data access layer in the form of a repository to query a SharePoint list and created a supporting tree data structure to re-organize the data returned from the repository, it’s time to combine the two in the business layer. Because we’re using the repository to abstract away storage details and the tree structure, as opposed to XML or tree view control APIs, to structure data, the business layer has become easier to reason about. In fact, the business layer can, or perhaps should, be developed as a console application or using a unit testing framework to ensure presentation independence and to work around the long SharePoint deployment and feedback cycle.

To make the code easier to follow, let’s assume the LegalDocumentRepository.GetDocuments method brings back this set of legal documents from the SharePoint list:

new LegalDocument { Title = "node-1", DocumentId = "node-1", ParentDocumentId = "root", Level = 2 },
new LegalDocument { Title = "node-1.1", DocumentId = "node-1.1", ParentDocumentId = "node-1", Level = 3 },
new LegalDocument { Title = "node-1.1.1", DocumentId = "node-1.1.1", ParentDocumentId = "node-1.1", Level = 4 },
new LegalDocument { Title = "node-1.2", DocumentId = "node-1.2", ParentDocumentId = "node-1", Level = 3 },
new LegalDocument { Title = "node-2", DocumentId = "node-2", ParentDocumentId = "root", Level = 2 }

The goal is to transform this set of documents into a tree by tying documents together using the DocumentId/ParentDocumentId properties. In doing so, we assume the parent of a document must always be at a lower Level than the child. Thus, building the tree is done by building lower levels first.

Here’s the business layer class that calls out to the repository and transforms the tabular result into the tree structure:

public class DocumentBrowser {
    public Tree<LegalDocument> GetDocuments(SPList l) {
        var repository = new LegalDocumentRepository();
        var documents = repository.GetDocuments(l);

        // in the real world, the root LegalDocument instance would also be retrieved from the list.
        var root =
            new Tree<LegalDocument>(new LegalDocument {
                Title = "root",
                DocumentId = "root",
                ParentDocumentId = "",
                Level = 1
            });

        return BuildTree(documents, root);           
    }

    public Tree<T> BuildTree<T>(IEnumerable<T> documents, Tree<T> tree) where T : LegalDocument {
        documents
            .OrderBy(d => d.Level)
            .ToList()
            .ForEach(d => {
                var p = tree.Find(pd => pd.DocumentId == d.ParentDocumentId);
                if (p != null)
                    p.Children.Add(new Tree<T>(d));
            });

        return tree;
    }
}

A console application that exercises the business layer looks like so:

public static void Main() {
    using (var siteCollection = new SPSite("http://intranet")) {
        using (var site = siteCollection.OpenWeb("Legal")) {
            var list = site.Lists["LegalDocuments"];
            var documentBrowser = new DocumentBrowser();
            var tree = documentBrowser.GetDocuments(list);
            System.Console.WriteLine(tree);

            /*
             root
               node-1
                 node-1.1
                   node-1.1.1
                 node-1.2
               node-2
            */
        }
    }
}

Ultimately, the layering introduces more classes and code, but the code becomes more abstract, less cluttered, easier to read, and faster to debug.