Click here to Skip to main content
15,881,882 members

Dominic Burford - Professional Profile



Summary

Follow on Twitter LinkedIn      Blog RSS
6,554
Author
2,053
Authority
9,852
Debator
8
Editor
100
Enquirer
212
Organiser
2,954
Participant
I am a professional software engineer and technical architect with over twenty years commercial development experience with a strong focus on the design and development of web and mobile applications.

I have experience of architecting scalable, distributed, high volume web applications that are accessible from multiple devices due to their responsive web design, including architecting enterprise service-oriented solutions. I have also developed enterprise mobile applications using Xamarin and Telerik Platform.

I have extensive experience using .NET, ASP.NET, Windows and Web Services, WCF, SQL Server, LINQ and other Microsoft technologies. I am also familiar with HTML, Bootstrap, Javascript (inc. JQuery and Node.js), CSS, XML, JSON, Apache Cordova, KendoUI and many other web and mobile related technologies.

I am enthusiastic about Continuous Integration, Continuous Delivery and Application Life-cycle Management having configured such environments using CruiseControl.NET, TeamCity and Team Foundation Services. I enjoy working in Agile and Test Driven Development (TDD) environments.

Outside of work I have two beautiful daughters. I am also an avid cyclist who enjoys reading, listening to music and travelling.

 

Reputation

Weekly Data. Recent events may not appear immediately. For information on Reputation please see the FAQ.

Privileges

Members need to achieve at least one of the given member levels in the given reputation categories in order to perform a given action. For example, to store personal files in your account area you will need to achieve Platinum level in either the Author or Authority category. The "If Owner" column means that owners of an item automatically have the privilege. The member types column lists member types who gain the privilege regardless of their reputation level.

ActionAuthorAuthorityDebatorEditorEnquirerOrganiserParticipantIf OwnerMember Types
Have no restrictions on voting frequencysilversilversilversilver
Bypass spam checks when posting contentsilversilversilversilversilversilvergoldSubEditor, Mentor, Protector, Editor
Store personal files in your account areaplatinumplatinumSubEditor, Editor
Have live hyperlinks in your profilebronzebronzebronzebronzebronzebronzesilverSubEditor, Protector, Editor
Have the ability to include a biography in your profilebronzebronzebronzebronzebronzebronzesilverSubEditor, Protector, Editor
Edit a Question in Q&AsilversilversilversilverYesSubEditor, Protector, Editor
Edit an Answer in Q&AsilversilversilversilverYesSubEditor, Protector, Editor
Delete a Question in Q&AYesSubEditor, Protector, Editor
Delete an Answer in Q&AYesSubEditor, Protector, Editor
Report an ArticlesilversilversilversilverSubEditor, Mentor, Protector, Editor
Approve/Disapprove a pending ArticlegoldgoldgoldgoldSubEditor, Mentor, Protector, Editor
Edit other members' articlesSubEditor, Protector, Editor
Create an article without requiring moderationplatinumSubEditor, Mentor, Protector, Editor
Approve/Disapprove a pending QuestionProtector
Approve/Disapprove a pending AnswerProtector
Report a forum messagesilversilverbronzeProtector, Editor
Approve/Disapprove a pending Forum MessageProtector
Have the ability to send direct emails to members in the forumsProtector
Create a new tagsilversilversilversilver
Modify a tagsilversilversilversilver

Actions with a green tick can be performed by this member.


 
GeneralBlog updates Pin
Dominic Burford13-May-21 1:37
professionalDominic Burford13-May-21 1:37 
GeneralPublishing events into an ASP.NET Core webhook with Octopus Pin
Dominic Burford13-May-21 1:33
professionalDominic Burford13-May-21 1:33 
GeneralA quick update Pin
Dominic Burford2-Nov-20 8:33
professionalDominic Burford2-Nov-20 8:33 
GeneralHow do you know when it's time to move on? Pin
Dominic Burford19-Aug-20 0:19
professionalDominic Burford19-Aug-20 0:19 
GeneralInitial thoughts on deploying with Octopus Pin
Dominic Burford17-Aug-20 4:25
professionalDominic Burford17-Aug-20 4:25 
GeneralWhat makes for a good DevOps process? Pin
Dominic Burford5-Aug-20 23:00
professionalDominic Burford5-Aug-20 23:00 
GeneralExecuting async code from non async code Pin
Dominic Burford19-Jul-20 23:21
professionalDominic Burford19-Jul-20 23:21 
GeneralPassing dynamic queries between client and backend server Pin
Dominic Burford29-Jun-20 4:55
professionalDominic Burford29-Jun-20 4:55 
GeneralManaging your Azure resources using Powershell scripting Pin
Dominic Burford13-Mar-20 6:42
professionalDominic Burford13-Mar-20 6:42 
GeneralIntroduction to Azure Cognitive Search Pin
Dominic Burford28-Feb-20 5:39
professionalDominic Burford28-Feb-20 5:39 
GeneralUpdating the version number in your .NET build pipeline Pin
Dominic Burford24-Feb-20 5:33
professionalDominic Burford24-Feb-20 5:33 
GeneralFinding a solution to an Azure web app deployment problem Pin
Dominic Burford23-Jan-20 23:51
professionalDominic Burford23-Jan-20 23:51 
GeneralStructured Logging Pin
Dominic Burford24-Dec-19 1:28
professionalDominic Burford24-Dec-19 1:28 
GeneralEnabling TLS 1.2 on your .NET application Pin
Dominic Burford11-Dec-19 23:11
professionalDominic Burford11-Dec-19 23:11 
GeneralThe new version of the app is (almost) ready for release Pin
Dominic Burford7-Nov-19 23:28
professionalDominic Burford7-Nov-19 23:28 
GeneralChunking your lists into multiple smaller lists Pin
Dominic Burford11-Sep-19 3:40
professionalDominic Burford11-Sep-19 3:40 
GeneralWriting flexible filters for your data using Predicates Pin
Dominic Burford16-Jul-19 6:11
professionalDominic Burford16-Jul-19 6:11 
GeneralRe: Writing flexible filters for your data using Predicates Pin
Slacker00716-Jul-19 22:09
professionalSlacker00716-Jul-19 22:09 
GeneralRe: Writing flexible filters for your data using Predicates Pin
Dominic Burford17-Jul-19 1:17
professionalDominic Burford17-Jul-19 1:17 
GeneralBlocking Asynchronous Code Pin
Dominic Burford4-Jul-19 22:11
professionalDominic Burford4-Jul-19 22:11 
GeneralDesigning and implementing flexible RESTful services Pin
Dominic Burford14-Jun-19 0:50
professionalDominic Burford14-Jun-19 0:50 
Following on from a couple of my previous articles, I would like to both reinforce the ideas I laid out in them, as well as consolidate those ideas. In an article[^] from October 2017 I described a pattern I use for designing and implementing RESTful APIs, specifically with regards to implementing RESTful GET APIs. In an article[^] from July 2018 I described the principle of reducing the client surface area, and how this leads to cleaner, simpler and less complex code, particularly with regards to implementing RESTful APIs.

Where I currently work, we have a library of RESTful ASP.NET Web APIs that our web and mobile applications consume. These cover many different types of query as they are used in many different ways by the particular applications. For example the mobile app (which is aimed at fleet drivers) fetches data for the currently signed-in user, their latest mileage updates, their account manager, journeys they have made etc. The web application fetches data relating to users, roles, permissions, documents etc.

These are all GET methods that perform a variety of different queries against different data types. When designing the client API surface required for all these APIs I wanted to make them all consistent, irrespective of what data was being returned, or what query filters were being specified.

To clarify the problem a little further, I wanted to use the same client API for all data types e.g. mileage, user, company, journey etc. Further to this, I wanted the way in which the data was queried to be consistent. Example queries are listed below.

- Fetch me the mileage data for this user
- Fetch me the mileage data for this date range
- Fetch me the journey data for this date
- Fetch me the journey data for this user
- Fetch me the permissions for this user
- Fetch me the documents for this user

These are all queries that work on different data (mileage data, journey data, permissions data, documents data) and interrogate the data in different ways (by user, by date). Crucially, I wanted all of these queries to map onto a single GET API for consistency, and to reduce the complexity of the client (by reducing the client facing API to one API instead of multiple APIs). Reducing the client facing API is the principle of reducing the surface area of the client.

I finally came up with the following API design.

- I have a single controller with a GET method that accepts two parameters.
- The first parameter is a string that designates the type of query e.g. "getmileagebyuser", "getjourneybydate" etc
- The second parameter is a serialised query object that contains the values needed to query (or filter) the data e.g. the user ID, the date or whatever filters are required to satisfy the request.
- All queries must return their data as a serialised string (which the client can de-serialise back into an object).

For the purposes of clarity the code examples used here have omitted error checking, logging, authentication etc to keep the code as simple as possible. In my own library of RESTful APIs I have separated out the requests made by the mobile app from those made by the web app. I therefore have two controllers, each with a single GET method that does all the heavy lifing of fulfilling the many different query requests. I have created a different controller for each type of client so as to prevent the controllers from bloating. You can separate out the requests any way you want. If you don't have many queries in your application, then you could simply place all of these query requests in a single GET method in a single controller. That is obviously a design decision only the developer can make.

The controllers are called MobileTasksController and WebTasksController. For the purposes of this article I will focus on the latter controller only, although they both employ the same design pattern that I am about to describe.

First let's define our basic controller structure.
C#
public class WebTasksController : BaseController
{
  public WebTasksController()
  {
  }

  public string WebGetData(string queryname, string queryterms)
  {
  }
}
You will need to decorate the WebGetData() method for CORS to allow the clients to make requests from your GET method.
C#
[HttpGet]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public string WebGetData(string queryname, string queryterms)
{
}
Enable CORS with the appropriate settings for your own particular application.

As we can see, the WebGetData() method has two parameters.
- queryname is a string that designates the type of query e.g. "getmileagebyuser", "getjourneybydate" etc
- queryterms is a serialised query object that contains the values needed to query (filter) the data e.g. the user ID, the date or whatever filters are required to satisfy the query request

Here's the class that I use for passing in the query filters.
C#
[DataContract]
public class WebQueryTasks
{
    [DataMember]
    public Dictionary<string, object> QuerySearchTerms { get; set; }

    /// <summary>
    /// Default constructor
    /// </summary>
    public WebQueryTasks()
    {
        this.QuerySearchTerms = new Dictionary<string, object>();
    }
}
At its core it comprises a dictionary of named objects. By implementing a dictionary of objects this allows us to pass in filters for any type of data e.g. dates, ints, strings etc. We can also pass in as many filters as we need. We can therefore pass in multiple filters e.g. fetch all the journeys for a specific user for a specific date. In this example, we pass in two filters.

- The user ID
- The date

Once the query is serialised we have the following string which is then passed as the second parameter to the RESTful GET method.
HTML
{"QuerySearchTerms":{"email":"test@mycompany.co.uk"}}
By implementing our queries in this way makes for very flexible code that allows us to query our data in any way we want.
C#
var user = GetUser(emailaddress);
WebQueryTasks query = new WebQueryTasks();
query.QuerySearchTerms.Add("userid", user.Id);
query.QuerySearchTerms.Add("journeydate", Datetime.Now);
string queryterms = ManagerHelper.SerializerManager().SerializeObject(query);
The WebGetData() method then needs to deserialise this object and extract the filters from within. Once we have extracted the filters we can then use them to fetch the data as required by the request.
C#
WebQueryTasks query = ManagerHelper.SerializerManager().DeserializeObject<WebQueryTasks>(queryterms);
if (query == null || !query.QuerySearchTerms.Any())
{
  throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, new HttpError("Unable to deserialise search terms.")));
}

The core of the WebGetData() method is a switch statement that takes the queryname as its input. Then, depending on the type of query, the method will extract the necessary filters from the WebQueryTasks parameter.

The names of the queries are stored as constants but could equally be implemented an an enum if preferred. We don't want to have to hard-code the names of our queries into the method, so any approach that separates these is fine.

In the example below there are two queries. One returns company data for a specified user. The second returns company data for a specified company ID. In each case the code follows the same pattern.

- select the appropriate case statement in the switch
- extract the filters from the query
- invoke the appropriate backend service to fetch the date using the extracted filters (after firstly checking that the filter(s) are not empty)
- serialise the data and return it to the client
C#
object temp;
string webResults;
switch (queryname.ToLower())
{
  case WebTasksTypeConstants.GetCompanyByName:
    webResults = this._userService.GetQuerySearchTerm("name", query);
    temp = this._companiesService.Find(webResults);
    break;
  case WebTasksTypeConstants.GetCompanyById:
    webResults = this._userService.GetQuerySearchTerm("companyid", query);
    int companyId = Convert.ToInt32(webResults);
    temp = this._companiesService.Find(companyId);
    break;
  default:
    throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, new HttpError($"Unknown query type {queryname}.")));
}
We then need to serialise the results and return these to the client.
C#
var result = ManagerHelper.SerializerManager().SerializeObject(temp);
return result;
In the production version of this controller, I have implemented many more queries in the switch statement, but for clarity I have only implemented two for the purposes of this article.

Here is the full code listing.
C#
[HttpGet]
[EnableCors(origins: "*", headers: "*", methods: "*")]
public string WebGetData(string queryname, string queryterms)
{
  WebQueryTasks query = ManagerHelper.SerializerManager().DeserializeObject<WebQueryTasks>(queryterms);
  if (query == null || !query.QuerySearchTerms.Any())
  {
    throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, new HttpError("Unable to deserialise search terms.")));
  }

  object temp;
  string webResults;
  switch (queryname.ToLower())
  {
    case WebTasksTypeConstants.GetCompanyByName:
      webResults = this._userService.GetQuerySearchTerm("name", query);
      temp = this._companiesService.Find(webResults);
      break;
    case WebTasksTypeConstants.GetCompanyById:
      webResults = this._userService.GetQuerySearchTerm("companyid", query);
      int companyId = Convert.ToInt32(webResults);
      temp = this._companiesService.Find(companyId);
      break;
    default:
      throw new HttpResponseException(Request.CreateResponse(HttpStatusCode.BadRequest, new HttpError($"Unknown query type {queryname}.")));
  }

  var result = ManagerHelper.SerializerManager().SerializeObject(temp);
  return result;
}
Just to repeat, for the purposes of this article, the method above has had all error checking, logging, authentication etc removed for the sake of clarity.

I have implemented this pattern in all my GET APIs to great success. It is very flexible and allows me to query the data in multiple ways as neccesary. It also allows the client code to be simpler too, by reducing the client area (the client only needs to interact with a single endpoint / controller), and enforces consistency by ensuring that all queries are similar to one another (they must all pass in two parameters - the first designating the query type, the second containing the query filters).

This pattern of API design achieves all the following benefits
- Simpler server side code by producing substantially less code due to the generic nature of the pattern
- Simpler client side code by only having a single endpoint to interact with
- High degree of flexibility by allowing the APIs to filter the data any way the application requires
- Consistency by ensuring that all requests to the RESTful API are the same

I have been using this pattern in my own RESTful APIs for several years, including several production mobile apps (that are available in the stores) and line-of-buiness web apps. With the pattern in place, I can quickly and easily add new RESTful APIs. This makes adding new services to the apps more timely, and makes the process of adding value to the apps much quicker and simpler.

Feel free to take this idea and modify it as neccessary in your own RESTful APIs.
"There are two ways of constructing a software design: One way is to make it so simple that there are obviously no deficiencies, and the other way is to make it so complicated that there are no obvious deficiencies. The first method is far more difficult." - C.A.R. Hoare

Home | LinkedIn | Google+ | Twitter

GeneralWriting asynchronous code with .NET Pin
Dominic Burford10-Jun-19 3:34
professionalDominic Burford10-Jun-19 3:34 
GeneralWeird Minification Behaviour in ASP.NET Core Pin
Dominic Burford28-May-19 3:54
professionalDominic Burford28-May-19 3:54 
GeneralThe Importance of Structure and Dilligence Pin
Dominic Burford24-May-19 6:14
professionalDominic Burford24-May-19 6:14 
GeneralWhen should you rewrite that legacy application? Pin
Dominic Burford22-May-19 0:03
professionalDominic Burford22-May-19 0:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.