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.
stores/:storeId is deprecated and put stores
does "deprecated" means that you don't provide backward compatibility? Or it still must exist and provide with any sort of message or redirection to the new interface?