The Problem Statement

Imagine that you have just built a sophisticated application to work against a SQL Server database. You followed a best-practices approach and separated out your presentation, business logic and data access layer. Your application is service oriented – and exposes a set of services to the outside world. For e.g. – your application offers an Account Service and a Security Service for consumption by the outside world.

  1. Now – you are told that this application needs to work against not just SQL Server – but Oracle as well.
  2. However, the services (AccountService, SecurityService) – will be somewhat different for the Oracle version. 
  3. While the services may be different, a lot of the functionality between the platforms will be similar.
  4. In addition, your design must be flexible (extensible). If more database platforms were added down the road – or more services to go along with the AccountService and the SecurityServices, the design should accommodate these with minimal changes.

This can be summarized as supporting multiple services for multiple platforms (these platforms do not have to be multiple databases – can be multiple OSes, multiple device platforms –e.g. Mobile and Desktop).   One of the key issues that this design presents is that of code commonality –  and code differentiation. Some of the code for the multiple platforms may be common to both the platforms. Example – when the AccountService creates an Account, there may be exception handling code that is common to both Oracle and SqlServer. In addition – a good deal of code would be completely unique to each platform – so – a SQLServer account creation would use SQLServer authentication whereas an Oracle account creation would use Oracle specific authentication.

This article presents an architecture to solve both of the problems above (keeping common code as well as differentiation platform specific code) – and provides a full sample implementation. This implementation can be used for various problems that require accommodating multiple services and multiple platforms. The possibilities are limitless – and I have used this on at least 2 projects successfully.  It allows for extensibility by allowing more services to be added – as well as more platforms to be added down the road.

Starting Point – Make it simple for the client

What would a sample client need to do to invoke a sample platform-specific service ? Say – a client wanted to use the AccountService specific to SqlServer. The snippet below shows everything that a client should need to do – get a handle to the SqlServer factory. This factory is responsible for all SqlServer specific services – so getting an AccountService back from this factory should be as simple as a GetService call. The snippet below shows everything that a client should need to do.

Code Snippet
  1.  
  2. ServiceFactory.SqlServerServiceFactory sqlFactory = new ServiceFactory.SqlServerServiceFactory();
  3. IAccountService acctService = sqlFactory.GetService<IAccountService>();

An Introduction to the overall architecture

To model the various services (AccountService, SecurityService…), we notice firstly that these services are unrelated to each other. A prospective implementation could implement both the AccountService and the SecurityService.  This leads us to explore an interface based approach for the set of services.

Services by Feature (Account, Security) Architecture – An Interface Based Approach

Code Snippet
  1. interface IAccountService
  2.   {
  3.       void CreateAccount();
  4.   }

 

Code Snippet
  1. interface ISecurityService
  2.     {
  3.         void AuthenticateUser();
  4.     }

Services By Platform (SQLServer, Oracle…) Architecture – An Inheritance Based Approach

For the platform differentiation, we note that some of the code may be common to both platforms – for e.g. – error handling and exception logging from the AccountService would need to be identical for both Oracle and SqlServer. We use an inheritance based approach – which allows us to use a Template pattern. The template pattern would provide a template method – that consists of a) Common Code b) Platform Specific Code

Code Snippet
  1. public virtual void CreateAccount()
  2. {
  3.  
  4.     //  common code – example – Initialize
  5.     Console.WriteLine("Account Service: Common Initialization Code");
  6.  
  7.     // Platform specific code
  8.     SomePlatformSpecificOperation();
  9.  
  10.     // common code again – example – cleanup()…
  11.     Console.WriteLine("Account Service: Common Cleanup Code");
  12.  
  13. }
  14.  
  15. protected virtual void SomePlatformSpecificOperation()
  16. {
  17.     Console.WriteLine("Some BASE operation that is common to both ORACLE and SQLServer. This can be over-riddent in the child classes for PLATFORM speficic functionality");
  18. }

The Overall Architecture – using Inheritance for Platform Variation – and interfaces for services

services_architecture_object_model_thumb

 

The Service Factory

The only other component (apart from the interface based services and the inheritance based platforms) that we need is a service factory – one that will return the appropriate feature service (e.g. Account Service) – along with the appropriate platform (e.g. SqlServerAccountService).

Code Snippet
  1. interface IServiceFactory
  2.     {
  3.        T GetService<T>();
  4.     }

A simple dictionary is used to store the list of services (AccountService, SecurityService…) – and a lookup method is provided as shown below:

Code Snippet
  1. private IDictionary<string, object> allservices = new Dictionary<string, object>();
  2.  
  3. public T GetService<T>()
  4. {
  5.     object service = null;
  6.     T returnedService = (T)service;
  7.     string serviceName = typeof(T).FullName;
  8.     if (allservices.TryGetValue(serviceName, out service))
  9.     {
  10.         returnedService = (T)service;
  11.     }
  12.  
  13.     return returnedService;
  14.  
  15. }

Summary

Almost every app today follows a service oriented architecture which exposes multiple services. If our app was limited to just that, the above interface based approach coupled with our Service Factory (to return a specific service) would be all that is needed. However, today’s services are not just feature based – but also tend to support multiple platforms. These multiple platforms could be multiple databases (e.g. SQLServer and Oracle on the backend), multiple devices (e.g. Desktop and Mobile), multiple OSes etc.

This post provides a full object oriented implementation that supports multiple platforms along with multiple services (features). The multiple services were modeled using interfaces. Then, to account for the different platform implementations of each service, we defined an inheritance hierarchy – coupled with a template pattern to handle code commonality between the different platforms.

  This proves to be an elegant solution for a common, real-world problem in Object Oriented projects. There are a multitude of possible applications of the above architecture. 

Perhaps the greatest strength of the above architecture is the extensibility it provides. One can add new database platforms and new services down the road – with ease and minimal code changes.

NOTE: Ideally, you would want to design a Data Access Layer that was agnostic of the underlying database. This layer would generate all your SQL statements (CRUD) without caring about what the underlying database platform was. Such a database agnostic data access layer is described in an earlier post here.

Full Solution Download

Services_By_Platform_Architecture Download Solution

Anuj holds professional certifications in Google Cloud, AWS as well as certifications in Docker and App Performance Tools such as New Relic. He specializes in Cloud Security, Data Encryption and Container Technologies.

Initial Consultation

Anuj Varma – who has written posts on Anuj Varma, Hands-On Technology Architect, Clean Air Activist.