1

I am working on a new API where we have requirement for many to many versioning.

  • Old Client -> New Server
  • Old Server -> New client
  • and everything in between

I've read some of the other posts about defensive programming and having DTOs that evolve gracefully... and we are definitely going to use that pattern in most cases.

However, when we have a breaking change for CreateCustomer and require CreateCustomer2 DTO, I would like to be able to customize the way SS resolves the type that is used to deserialize, otherwise we will be forced to use routes like this:

/api/v1/createcustomer

/api/v2/createcustomer

Where I would much rather keep it /api/createcustomer and let the version number live in the Header / Querystring / whatever (not looking for a debate here :) ).

Are there any examples of how to resolve the deserialization type dynamically in ServiceStack, based on a parameter other than route + verb?

Many thanks in advance

jglassco
  • 133
  • 11

1 Answers1

1

The recommended approach for versioning is to take advantage for the natural forwards compatibility of message-based services and extend existing services defensively so it can support multiple client versions and avoid create multiple versions of the same service.

If you still want to expose /api/v1 routes than I'd recommend doing it at the infrastructure level by using a reverse proxy to re-route /api/v1 and /api/v2 requests to different running instances of ServiceStack.

If you want to do the proxying in code you can use base.ResolveService<T>() or base.ExecuteRequest() to execute different Services in code and ServiceStack's built-in AutoMapping to populate different DTO's which could look something like:

[Route("/customers")]
public class CreateCustomers {
    public int Version { get; set; }
}

public class CreateCustomersV1 { ... }

public class CreateCustomersV2 { ... }

public class CustomerServices : Service
{
    public object Any(CreateCustomers request)
    {
        return request.Version == 1
            ? base.ExecuteRequest(request.ConvertTo<CreateCustomersV1>())
            : base.ExecuteRequest(request.ConvertTo<CreateCustomersV2>())
    }
}
Community
  • 1
  • 1
mythz
  • 141,670
  • 29
  • 246
  • 390
  • I tried your code proxy sample - it is close to what I am looking for - though the members of CreateCustomersV1 and V2 do not get initialized properly (ie. it seems the deserializer skips them or ? ) and they are always null after calling request.ConvertTo(). Am I missing something here? Thanks again. – jglassco Jun 09 '15 at 21:20
  • My dev team is making a decision about versioning very soon.. apologies for another message. We've already burned so much time exploring options. In order to implement versioning where the route always stays the same (even for incompatible versioned DTOs), I need to be able to dynamically resolve the deserializer that is used based on path, verb and our (custom header etc) version. Right now this does not seem possible based on my knowledge of service stack. (I thought I was on the right track with ServiceController.RequestTypeFactoryMap?) Is this possible now or in the SS roadmap? – jglassco Jun 10 '15 at 18:30
  • @jglassco 1 Route always maps to 1 Request DTO, the example above shows one technique on how to proxy the request into different Services. Although the [recommendation is to evolve existing Services](http://stackoverflow.com/a/12413091/85785) or alternatively use a reverse proxy to route versions at the infrastructure level as maintaining multiple versions of typed DTO's, Services, DAO's, Validators, etc is especially cumbersome in a statically typed language. – mythz Jun 11 '15 at 01:36
  • ok - I've accepted that keeping the route the same, with different requested versions of a DTO is not possible - in cases where we break compatibility we will have to v2, v3, etc the route. Thanks for your help. – jglassco Jun 11 '15 at 15:54