9.13.2013

Encapsulated Request Pattern

Many times we need to make requests to external systems when integrating applications. I've noticed a pattern I like when making these requests that simplifies understanding what all is involved.

Procedural submission

It's easy to end up with procedural code to execute requests, something like:

// detect condition to send remote request
    request = BuildRequest()
    client = CreateClient()
    response = client.Submit(request)
    if(response.Error) throw new Exception(...)
// continue with our code

Right in the middle of our application code, we conditionally decide if we need to submit a request, then we build, send and validate it all inline in our application code. Sometimes we make this one step better by extracting functions to deal with the request in some static class or injected service. This is compounded when we have multiple request types and we start to try to re-use parts of the process and end up with conditionals bleeding into the consumer code:

// detect condition to send remote request
    request = BuildRequest()
    client = CreateClient()
    response = SubmitRequest(client, request)
    if(response.Error) throw new Exception(...)
// continue with our code

// SubmitRequest(client, request)
    if(request is ThisTypeOfRequest)
        return SubmitThisTypeOfRequest(client, request);
    if(request is ThatTypeOfRequest)
        return SubmitThatTypeOfRequest(client, request);

// SubmitThisTypeOfRequest(client, request)
    // special code for This Type
    return client.Submit(request);

// SubmitThatTypeOfRequest(client, request)
    // special code for That Type
    return client.Submit(request);

This can get pretty hard to manage, and in some cases validating the response becomes conditional too and often this happens:

// detect condition to send remote request
    request = BuildRequest()
    client = CreateClient()
    response = SubmitRequest(client, request)
    if(response is ThisTypeOfResponse && response.Error) throw new Exception(...)
    if(response is ThatTypeOfResponse && response.InvalidWhatever) throw new InvalidException(...)
// continue with our code

Sometimes that response handling code at least ends up in the Submit methods that are extracted, either way it's pretty hard to follow as the parts of each request (building, submitting, validating) are scattered throughout some highly procedural code. It becomes hard to follow and prone to duplication and mistakes :(.

Encapsulated requests

One way I've found to help make things more apparent is to create a type per request. Like an anti-corruption layer around the request. In here I'll build, submit and validate each request:

// detect condition to send remote request
    request = new ThisTypeOfRequest()
    request.Submit()
// continue with our code

or

// detect condition to send remote request
    request = new ThatTypeOfRequest()
    request.Submit()
// continue with our code

class Request
    function Submit()
        request = Build()
        client = CreateClient()
        response = client.Submit(request)
        Validate(response)

class ThisTypeOfRequest extends Request
    function Build()
        // special code for This Type
    function Validate(response)
        if(response.Error) throw new Exception(...)

class ThatTypeOfRequest extends Request
    function Build()
        // special code for That Type
    function Validate(response)
        if(response.InvalidWhatever) throw new InvalidException(...)

Now, everything involved with a single request is in one class, in one file, one place to see what is going on! And in some cases, things like Submit can be shared across request types, and if it varies, it can be overridden. This pattern brings readability and re-use to the solution. Now consumers don't have to manually compose building, submitting and validating requests, they can focus simply on which requests to execute.

Steps to refactor to Encapsulated requests

If you have procedural code like this, try these steps to gradually refactor to this pattern:

  • Extract conditional building code into a single Build method that all consumers use
    • Then, in time, remove the conditionals and move the Building code for each request type into it's own class
  • Do the same for conditional submission code
  • Do the same for conditional validation code
  • Have patience to refactor in this direction over time, don't worry about doing this all at once.

Composing requests

Sometimes an event in our system triggers a need for multiple requests, almost as if the composite request doesn't exist in the external system. It becomes trivial to compose requests with this pattern:

class CompositeRequest extends Request
    function Build()
        _ThisRequest = new ThisTypeOfRequest()
        _ThatRequest = new ThatTypeOfRequest()
    override Submit()
        _ThisRequest.Submit()
        _ThatRequest.Submit()
    function Validate(response)
        // maybe nothing to do here

Now we can easily compose, with conditionals, multiple requests! Imagine if consumers had to compose this every time they needed this series of requests!

Analysis of pattern

  • Pros
    • Readability, one stop spot to see what all is involved with a request
    • Re-use
    • Avoids mistakes when consumers don't ensure all steps are executed
    • Composable
  • Cons
    • Time to refactor in this direction
    • Getting crazy with inheritance re-use. I'd recommend using a common interface (Build/Submit/Validate) but beyond that use composition.




comments powered by Disqus

Related Posts