3

In my existing code, I have an interface like so:

public interface IStudentService
{
    T GetStudent(int userId);
}

This interface is called in many places, and the concrete implementation, let's call it StudentServiceDb, reads the student from the database.

Then I have a new requirement, which is to get the student info from an API, so now I need not only the userId, but I need to pass in the token from the caller as well.

Should I change the interface to

public interface IStudentService
    {
        T GetStudent(int userId, string token);
    }

And create StudentServiceAPI concrete implementation, and ignore token in the existing StudentServiceDb?

There is also an additional requirement that whether it reads from the database or from the API is determined by the feature flag, how should I handle it? Should I change all occurrences of IStudentService to IStudentServiceChooser which will register the both concrete implementations and evaluate which to use?

2
  • 2
    Why is it necessary to pass the token for each call of GetStudent? Shouldn't StudentServiceAPI allow to keep the token as internal state and provide this for several calls of "GetStudent"?
    – Doc Brown
    Dec 5, 2020 at 1:19
  • The token is not passed from the controller's httpContext header. The caller of studentService has to get a refresh token based on the sessionId that is saved everytime.
    – Miss
    Dec 5, 2020 at 11:55

2 Answers 2

4

Both the token, and the choice of which concrete implementation is needed should be handled at the time the service object is initialized.

The factory design pattern can be used to determine the concrete type that gets instantiated. This factory object can read feature flags, configuration — whatever — to determine which type to use. Since the factory object does the initialization, it should also have knowledge of the token used for the API, as well as the connection string used to connect to the database. The connection string should be passed as a constructor argument to the DB class, and the token should be passed as a constructor argument to the API class.

public class StudentServiceFactory
{
    public IStudentService GetStudentService()
    {
        if (...)
            return new StudentServiceDb(connectionString);
        else
            return new StudentServiceAPI(token);
    }
}

The factory object will likely be initialized near the composition root of your application. This would be the Main(string args[]) method, or an event handler responding to the start of the application, or the beginning of an HTTP request, if building a web application.

Every time you need the student service you will need the factory. This can be avoided if you utilize dependency injection.

When using DI, objects requiring a student service need only accept an IStudentService parameter in their constructor. A DI framework can handle the decision of which concrete type to use, because most DI frameworks allow you to register a factory object for an interface in the DI container.

0

Greg Burghardt's answer is actually what I had mind, too. However, from what you wrote it is not clear if this works in your case with a fixed token for each StudentServiceAPI object.

In case it does not, instead of passing a fixed token into the StudentServiceAPI constructor, consider if it is possible to inject a function into StudentServiceAPI which provides the correct token dynamically for each call:

class StudentServiceAPI
{
   public StudentServiceAPI(Func<string> tokenProvider)
   {
        this.tokenProvider=tokenProvider;
   }

   T GetStudent(int userID)
   {
       string token=tokenProvider();
       // ... return student object from API ...
   }
}

That way, you can keep the existing interface even if the token is not fixed for the service.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.