jeudi 29 mars 2018

A/B test clients from controller

I am currently using API's from client A in my controller. Sample definition of client A:

class A @Inject()(
    ws: WSClient)
{
  def createSession(request: CreateSessionRequest)
    : EitherT[Future, AppError, ResponseFromA] = 
 {
    //logic. Returns ResponseFromA case class
 }
}

My controller code is:

class MyController @Inject()(
    a: A) {
  def createSession(): Action[JsValue] =
    action.async(parse.tolerantJson) { request =>
      val requestValidation = for {
        request <- EitherT.fromEither[Future](
          checkRequest[CreateCheckout](request.body.toString, mapper))
      } yield {
        request
      }

      val createSessionResult = for {
        createCheckoutInput <- requestValidation
        createSessionResponse <- a.createSession(
          CreateSessionRequest(createCheckoutInput))
      } yield {
        createSessionResponse
      }

      // postProcessing creates a transfer object `SessionResult`
      // from `ResponseFromA`
      postProcessing(createSessionResult) 
}

Now I am planning to migrate to another set of API's which provide similar functionality but different response format. I have created a different client B as follows:

class B @Inject()(
    ws: WSClient)
{
  def createSession(request: CreateSessionRequest)
    : EitherT[Future, AppError, ResponseFromB] = 
 {
    //logic. Returns ResponseFromB case class
 }
}

Notice I have still used CreateSessionRequest in client B as well. This is because input to both set of API's is the same. I would like to A/B test both set of API's. Ideally I would like to use the same controller because controller has a lot of logic which I would need to replicate if I created another controller for client B. Response from my controller would also still be SessionResult because I want to keep these migration changes transparent to all clients using my service.

I was looking at different design patterns like the factory pattern to conditionally select the right client to call based on some rule (say a config value which says to use client B) but I am reaching a dead end because createSession from both clients have different return values. I was also thinking of creating a common business object which I could use to massage the ResponseFromA and ResponseFromB to the common business object but that means I would need to change postProcessing(createSessionResult).

Any tips or pointers on how I could conditionally use client of my choice without changing controller logic too much would be greatly appreciated.

Aucun commentaire:

Enregistrer un commentaire