It has been recommended to favor defensively evolving a DTO over time when versioning endpoints, but I'm finding it difficult to do without losing what I consider some key beneficial functionality provided by ServiceStack.
I am currently using ServiceStack v3, but can upgrade to v4 if/when necessary.
When implementing my Service, I can specify multiple implementations of Get() with different contracts, and ServiceStack maps the data coming in accordingly.
Works:
public object Get(EntityBrowse) { ... } // returns multiple of a single entity
public object Get(Entity) { ... } // returns a single entity
Also Works:
public object Get(Contracts.v1.Entity) { ... }
public object Get(Contracts.v2.Entity) { ... }
Does not work:
public object Post(Contracts.v1.Entity) { ... }
public object Post(Contracts.v2.Entity) { ... }
This case doesn't work to the point where all POSTs that come through this service are mapping to the v1 contract, even though the fields don't match. Even the swagger docs are showing the wrong v1 properties, but the correct summary/notes from the v2 DTO.
I would like to have a separate DTO for each major version of a given endpoint for a few reasons:
Swagger. Swagger docs generated from DTOs with a lot of fields can be confusing to the end users of public APIs. How does the user know which fields are for the version of the endpoint they want to use? I could document this per field, but I think it is easier to show the end user only the fields they care about at the time. Different customers will use v2 without ever knowing v1 existed.
Validation. ServiceStack provides validators per Type. This is perfect, except that if my DTO's required fields may drift over time, I can't continue using the same validator without some special casing. Maybe this is an acceptable loss?
Deprecation. After a given time, v1 will be deprecated. v1 represents the legacy implementation of the endpoint, before there was versioning, and before there were consistent contracts between entities (eg, using "Name" vs "Title", "TypeId" vs "Type"). Evolving a DTO over time after this seems more reasonable, but while v1 exists, the endpoints are restricted by the decisions developers made possibly up to a decade ago.
After reading over this a few times, I'm thinking that maybe I should be creating separate services to support older functionality.
The key differences between versions of my endpoints are:
Should I consider breaking my versions out in to separate services? Should I load up a single DTO with all the fields and just outline the supported version per property?
Use Swagger Inspector to quickly generate your OAS-based documentation for existing REST APIs by calling each end point and using the associated response to generate OAS-compliant documentation, or string together a series of calls to generate a full OAS document for multiple API endpoints.
Swagger is a set of open-source tools built around the OpenAPI Specification that can help you design, build, document and consume REST APIs. The major Swagger tools include: Swagger Editor – browser-based editor where you can write OpenAPI specs. Swagger UI – renders OpenAPI specs as interactive API documentation.
I'd strongly recommend against using multiple versions of the same Request DTO in the same Service, you should instead version your DTO's defensively so your Request DTO can support multiple versions. It's especially bad trying to maintain multiple versions of the same Service in a statically typed language which causes a lot of friction, duplicated code, maintenance, etc.
If you must maintain different versions, I'd recommend maintaining and hosting different branches and have a reverse proxy to redirect /v1/
and /v2/
API requests to old and new ServiceStack instances. That way once all customers have migrated off v1 you can drop it and your v2 will be clean without legacy v1 cruft infecting the current code base. But my recommendation is to version the same Request DTO defensively so you don't have to maintain multiple versions, ServiceStack's message-based design makes this a lot easier. It will also reduce confusion both inside and out, trying to maintain multiple versions is confusing for Developers maintaining the Service and anyone consuming them.
In ServiceStack each Request DTO Type should be unique and only have a single implementation, however most metadata services require the Request DTO name to be unique as well and many of the languages in Add ServiceStack Reference also requires all DTO's to be unique which is our recommendation. If you absolutely must have different types with different versions append the version on the Request DTO, e.g. GetEntitiesV1
and GetEntitiesV2
- these names will be hidden from users using your published Custom Routes.
ServiceStack's Swagger Support has seen a lot of updates in v4, if you see issues in v3, you can use the Free Quotas in v4 to see if the issues have been resolved.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With