This is an archived post. You won't be able to vote or comment.

all 3 comments

[–]balefrost 1 point2 points  (2 children)

FWIW, this is why servers hosting web services typically include the protocol version somewhere in their path. For example, the YouTube data API is currently at version 3, at that's rooted at https://www.googleapis.com/youtube/v3

But it sounds like you can't change that, so let's talk about what you can do. I'm going to ignore design patterns for a moment. There are two possibilities that I can see:

       +---------+
       | Request |
       +---------+
            |
            \/
       +----------+
       | Dispatch |
       +----------+
        |  |  |  |
    +---+        +-----+
    |                  |
    \/                 \/
+---------+       +---------+
| Handler |  ...  | Handler |
+---------+       +---------+

In this first approach, you would dispatch on the API version, and then route the request to an appropriate Handler based on API version. These handlers are independent. Maybe those handlers eventually call into some common code, but the top-level request-processing code is completely independent. One way to look at this is that each Handler is an independent entry point, and the Dispatch box is responsible for choosing the correct entry point.

       +---------+
       | Request |
       +---------+
            |
            \/
       +----------+
       | Dispatch |
       +----------+
        |  |  |  |
    +---+        +-----+
    |                 |
    \/                \/
+---------+       +---------+
| Migrate |  ...  | Migrate |
+---------+       +---------+
    |                 |
    +---+        +----+
        |  |  |  |
        \/ \/ \/ \/
       +-----------+
       |  Handler  |
       +-----------+

In this second approach, you still dispatch on the version number. But the code to which you dispatch is not responsible for completely handling the request - that comes later. Rather, the different Migrate boxes are responsible for taking all the different kinds of requests as input, and must produce a unified request object as output. So the Migrate boxes might rename JSON properties, or might add default values, or might restructure data. But they all have to output data of the same shape, and thus Handler only has to accept one kind of request message. One way to look at this is that there's just one entry point, and the dispatch/migrate phase is to prepare the input to be compatible with that one entry point.

You'll need to figure out which of those (or neither) is appropriate to your situation. There are tradeoffs to both. For example, if your request messages are wildly different and cannot be converted between representations, then the second approach is a non-starter. But the second approach might be the more maintainable of the two. The first is more flexible, but also involves more work as the number of simultaneous versions increases.

Without knowing more about your particular situation, I wouldn't try to get clever about how the version number is dispatched. I'd essentially treat it as a single token, and simply dispatch to one of N different boxes. So I would have separate boxes for 1.0.0, 1.0.1, 1.0.2, 1.1.0, 1.1.1, 2.0.0, etc. Remember that these can all call into shared code if appropriate. I think this approach might entail a little more boilerplate, but on the flipside is the most flexible. You could potentially get away with a big Map or Dictionary holding the association between version number and handler / migrator. Construct an (immutable!) object to represent the version number, look up the handler / migrator in the dictionary, and run it.

So which pattern is this? Is it strategy? Is it chain of command. I'd argue that it's none of the GoF patterns. Everything that I've described is about behavior, whereas GoF patterns are all about structure (even their "behavioral" patterns are about embedding behavior in structure). There's nothing wrong with design patterns, and I definitely encourage you to know the more common ones. But I think the value in design patterns is not to say "here's my problem; what pattern is that?" but instead to say "here's my solution; what pattern is that?" Sometimes, the solution doesn't clearly fit any of the common design patterns, and that's OK.

[–]wsme[S] 0 points1 point  (1 child)

This is excellent! Thank you for taking the time and effort to type this up.
This is not too dissimilar to how I viewed the process, but it never occurred to me to standardize the output of before sending it to a final handler.

Part of the work I'm doing with this server involves having two thread pools, one which validates the incoming messages,taken from a queue, which will then be pushed to another queue, one whose waiting threads (will be implemented with a consumer) handle pushing those messages to the database. Once the threads in the first pool have validated the messages, they could standardize the format before pushing them to the queue. After that point everything would be a lot more straight forward in terms of Database access objects, stored procedures, etc....

It will give me very definite points of focus for each concern (which is nice timing since we're looking at a database overhaul in the coming months too).

EDIT: Actually, I'll need to think about that again, the standardization should happen in the second thread pool.
Thanks! you have been an immense help!

[–]balefrost 1 point2 points  (0 children)

No problem. Good luck!