Writing flexible code

As part of a previous project I had to implement a login service for a mobile app. I wrote the service using ASP.NET Web API in C#. The service returned a user JSON object if the login was successful. All well and good. As part of my current project, I have a need to write another login service for a completely different application. This time I need to write a login service for a web application.

Looking at the requirements of the new web app login, it occurred to me that the code would be very similar to the mobile applogin service. They both needed the ability to locate a user (based on their email address) and check if the email that was entered by the user matched the email held in the User table in the database.

So I began thinking how I could make my login code more flexible and generic so that I could accomplish the same login functionality using different user types (a mobile app user and a web app user). I eventually came up with a solution that uses interfaces and generics, and gives me the flexibility to write multiple login services for any user type.

The following code is based around an n-tier architecture where there is a data abstraction layer that handles all interaction with the back-end database, and a business abstraction layer that enforces the domain logic rules.

Before going any further, for clarity I have deliberately omitted any code relating to authentication, security, permissions etc. When reading through the code, please bear this in mind. Do not copy and paste the code as-is.

So let’s start off by describing how the data has been made flexible. For this I created an interface that would be implemented by the data classes responsible for fetching the user from the User table.

Hide Copy Code

public interface ILoginData<TEntity, TModel>
{
TEntity FindByUsername(TModel model);
}

The TModel represents the user model that is passed into the function, and TEntity is the user that is returned from the User table. Both of these objects should be POCO (Plain Old C# Object) objects. Although I have specified a different type for the parameter and the return type, you could quite easily have the same object for both. I wanted to keep them separate so that the input and output objects can be different.

An example impementation of TModel could be as follows.

Hide Copy Code

public class UserModel
{
public string Email { get; set; }
public string Password { get; set; }
}

A similar definition can be used for the TEntity object too.

Any class that implements this interface therefore has to provide a mechanism for locating a user. This is the first step towards implementing our flexible login service.

An example class definition of a data class that implements our interface is as follows.

Hide Copy Code

public class UsersData : ILoginData<UserEntity, UserModel>
{
public UserEntity FindByUsername(UserModel model)
{
UserEntity result = null;
if (!string.IsNullOrEmpty(model?.Email))
{
result = this.GetUserByEmail(model.Email);
}
return result;
}
}

Next we need to implement a business service that invokes our data layer class. We need to implement another interface to ensure that each of our login classes each contain the same method.

Hide Copy Code

public interface ILoginService<T>
{
T LoginUser(string username, string password);
}

This is the actual login method that is being declared here. Each login class must implement a function called LoginUser that will return a user object. The parameters in this particular example are the username (email) and password, but your own function may take different parameters as required.

Hide Copy Code

public class WebLoginService : ILoginService<UserEntity>

//ensure that our data class implements our interface from earlier so that we can
//guarantee that is contains the method FindByUsername()
private readonly ILoginData<UserEntity, UserModel> _data;
    public UserEntity LoginUser(string username, string password)
{
UserEntity result = null;
        if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
{
UserModel user = new UserModel
{
UserName = username,
Password = password,
};
result = this._data.FindByUsername(user);
if (result != null)
{
//check if the user's email address and the entered password matches the one in the database
result.IsAuthenticated = string.CompareOrdinal(password, result.Password) == 0;
}
}
return result;
}
}

Here’s an example implementation of our interface. Note the line of code below.

Hide Copy Code

result = this._data.FindByUsername(user);

This is invoking our data class from earlier. We know that our data class contains this method because the reference to our data object is of type ILoginData<UserEntity, UserModel>.

I have implemented two login classes using this design (a mobile app and a web app). It gives me the flexibility to substitute different models and entities into the code. I have also implemented the following constructor which allows me to pass in different implementations of the data class so that I can unit test the login functionality without having to touch the database.

Hide Copy Code

public WebLoginService(ILoginData<UserEntity, UserModel> data)
{
this._data = data;
}

I invoke this constructor from my unit tests which contains a different implementation of my data class.

This design can be used for implementing other functionality besides login code. In fact, it can be used whenever you have any dependancy between one class and another class. Instead of relying on concrete types, you are relying on an interface and coding to that instead. It gives you the ability to provide different types of models and entities, as well as fully supporting unit testing.