How to Structure Files for API Versioning

apidomain-driven-designrest

My first time trying to implement API versioning here, and I need some guidelines on how to do it…

I know there are many ways of doing API versioning.
I'm already decided on doing it by uri (i.e, I'll have something like put mydomain.com/api/v1/resources/:resourceId)
The question is not about 'referencing' the right version. Instead, what I'm trying to understand is how to organize and structure the artifacts…

My concrete problem is the following:

I have an endpoint put stores/:storeId which is already being used by a front-end application. Then I realised it would be safer and simpler for validation if I took storeId implicitly from req.user. So my endpoint would change to be just put stores

If it was already in production, the front-end application would have to change. That made think I should be prepared for situations like this, and started to look into how to version my API.

But if an endpoint is controlling some artifacts and its behaviour changes, it seems to me that in order to isolate the previous endpoint from future changes necessary to newer versions, all the artifacts used by the previous endpoint should also be versioned. Right?

Because if we don't and we have different API versions expecting different behaviours from a same factory or repository, that would be a problem.

If that assumption is correct, so how would I structure my artifacts? Should I replicate the whole structure for each API version? Or should I version only the elements that change from version to version?

I'll try to give a concrete exemple below:

My Back-end's file structure

I'm using DDD and have my domain layer artifacts structured like:

src/domain/entityType/modelName/artifacts

where:

  • domain could be the Core domain (with the main models and rules),
    Actors domain (modelling the actors and permissions) or a Support
    domain (entities for logging and other support roles)
  • entityType would be a service, factory or repository
  • modelName are the main business entities of the domain artifacts would be files for the Mongoose schema, rules specifications, adapters, and the source code for factories, repositories or services that use those artifacts, with their respective test files as well

Example

For example, I could have a service which would allocate tasks to an employee. That service would be structured in:

src/core/services/taskAllocation/

where I would have taskAllocation.service.js, taksAllocation.rules.js

And taskAllocation.service.js would reference taskAllocation.rules.js and also src/core/repositories/employee/employee.repo.js and src/core/repositories/tasks/tasks.repo.js

And my application layer is structured like:

src/routes/domainName/modelName

where I'll have modelName.router.js, modelName.controller.js, modelName.validations.js (with rules for input validations)

The router contains the use cases associated to the specific modelName. And the controller will access and coordinate those src/domain entities defined above.

For the case in the example, there would be a folder src/routes/employess/employees.router.js. That would be accessed through an endpoint post employees/:id/tasks, which would call employeesController.allocateTasksById(req.employee.id), which would call the respective service method in taskAllocation.service.js at the domain layer.

The replication problem

This is how I have done so far.
Now suppose I will create versions for the API, like:
src/routes/v1/domainName/modelName and src/routes/v2/domainName/modelName
And the only difference in those APIs is now my original put stores/:storeId is deprecated and put stores should be used instead, which uses storeId from req.user.storeId.
No artifact has changed. The change is restricted to the application layer.

But if I keep both controllers (from API v1 and v2) accessing the same domain layer artifacts, someday someone could change some artifact and invalidate v1. It seems to me that the artifacts should be replicated (that would be an "accidental" duplication, right?, not a true one…)

And I thought about 2 options: either I version and replicate the whole src/domainName structure, like src/v1/domainName, or I could version and replicate only the domain artifacts referenced by the changed endpoint, which would be:

src/core/repositories/employees/v2
src/core/repositories/tasks/v2
src/core/services/tasksAllocation/v2

Well, as I said, it's my first time api-versioning…
In all the articles I've read about api versioning, I only saw the "naming" problem being talked about. I've never seen anything about managing and structuring the artifacts.
So guide me here, please.

Best Answer

Personally, I wouldn't store multiple API versions in one codebase. This leads to many trouble when there are differences in the business rules of different versions.

Instead, I create maintenance Git branches for each running API version. For example:

  • master: the current API version (e.g 1.4)
  • maintenance/1.3: the code of 1.3.x version
  • maintenance/1.2: the code of 1.2.x version

On the production environment, each maintenance branch is deployed to a dedicated server(s). A load balancer stands in front of them to route the traffic to the correct server(s) based on the version in the URI.

When new patches are released, I used cherry-pick to apply the fix to all branches that the patches apply to. Because my product API is also consumed by external clients, we have a communication channel to announce to the clients when an old version was deprecated.

The most important thing is to follow the semver and keep the backwards compatibility inside one maintenance branch.