belgif / rest-guide

REST Guidelines of Belgian government institutions
https://www.belgif.be/specification/rest/api-guide/
Apache License 2.0
24 stars 4 forks source link

composite keys to identify resources #105

Open pvdbosch opened 1 year ago

pvdbosch commented 1 year ago

REST API designers sometimes struggle to represent resources that are defined by a composite business key with a proper URI.

E.g. in case of a monthly declaration of a an employer about an employee, these are possible:

1) /employers/{employerId}/employees/{employeeId}/declarations/{month}

2) /declarations/{employerId}-{employeeId}-{month}

3) /declarations/{delcarationId}
/declarations?employer=...&employee=...month=... should be used to retrieve declarationId

declarationId may be formed as the same composite key as 2 under the hood, but this is not part of the contract


Current documentation (not a rule): https://www.belgif.be/specification/rest/api-guide/#document

When defining child resources, stick to concepts within the same API. An employer resource could contain child concepts as employees, debts, taxes, mandates, declarations, risks, but putting all these different concepts below a single document resource becomes unmanageable. In that case prefer associations and create [links](https://www.belgif.be/specification/rest/api-guide/#links) to other APIs from the employer resource.

pvdbosch commented 1 year ago

Zalando's guideline MAY expose compound keys as resource identifiers on this topic is not very clear to me.

Examples both with slashes as colon as separator. When using slash, should the parent path also resolve? Should the OpenAPI spec specify all compound key components separately but clients should use them as a whole to allow evolvability? Why not make them entirely opaque then by not documenting how it splits in components?

Note: Exposing a compound key as described above limits ability to evolve the structure of the resource identifier as it is no longer opaque. To compensate for this drawback, APIs must apply a compound key abstraction consistently in all requests and responses parameters and attributes allowing consumers to treat these as technical resource identifier replacement. The use of independent compound key components must be limited to search and creation requests, as follows ...

wsalembi commented 11 months ago

Axway now supports colon separators in resource paths from SP Aug23 and using patch API Gateway 7.7.20230228 Patch 30438 (allOS) so the platform is ready to support it.

e.g. /reports/{year}:{month}

The composite identifier is still 1 path segment respecting our design of document resource. Using slashes the identifier would be spread into multiple path segments.

pvdbosch commented 11 months ago

Guidelines we may consider:

pvdbosch commented 5 months ago

Context: the identity/uniqueness of a resource is determined by the combination of multiple immutable properties

design options:

composite identifier = identifier composed from multiple property values, e.g. {employeeId}:{month}

0) Introduce new resource identifier which is not composite: +: clients cannot do string manipulation +: stable when the business rules around uniqueness change -: two-step lookup -: new identifier values have to be generated and managed, with checks that the properties are still unique

1) Opaque composite identifier: the business identifier is technically composed from multiple values, but this is undocumented and opaque when using the API.

request : /declarations?employeeId=12345&month=2024-12

response:

{
  "items": [ {
    "id": "12345:2024-12"  # "type: string"
    "employeeId": 12345,
    "month": "2024-12"
   }
  ]
}

Once id is found, it can be used in direct resource lookup:

GET /declarations/12345:2024-12  # /declarations/{declarationId}                 

{
    "id": "12345:2024-12"  # "type: string"
    "employeeId": 12345,
    "month": "2024-12"
}

+: adaptable when the business rules around uniqueness/identity change +-: clients might do string manipulation which would prevent evolvability, but unlikely if not documented -: two-step lookup

2) Partially opaque composite key: composition is documented only for the document resource URL:

request: /declarations/{employeeId}:{month} /declarations/12345:2024-12

response:

{
  "id": "12345:2024-12"
  "employeeId": 12345,
  "month": "2024-12"
}

-: not adaptable when the business rules around uniqueness/identity change +: no two-step lookup of resource

3) Non-opaque key parts: only the individual components are documented and used

request /declarations/{employeeId}:{month} /declarations/12345:2024-12

response:

{
   "employeeId": 12345,
   "month": "2024-12"
}

-: not adaptable when the business rules around uniqueness/identity change +: no two-step lookup of resource -: more complex identity checks bc no single identifier string

alternative representation: matrix-style parameters

Matrix-style parameters might be useful when there are multiple parts that can be optionally chosen from, e.g.

GET /actors/;enterpriseNumber=1234
GET /actors/;ssin=123456789

Alternative in path-param style: GET /actors/enterpriseNumber:1234

WG:

For next WG, I'll work out some alternatives regarding to (non/partially/fully) opaque identifiers and matrix parameters.

pvdbosch commented 5 months ago

On matrix params:

From https://www.w3.org/DesignIssues/MatrixURIs.html:

Just as the slash separated set of elements is useful for representing a tree, so a set of names and equally significant parameter can represent a space more like a (possible sparse) matrix. In this case navigation to "close" locations is done by varying one or more parameters which form the dimensions of the matrix. This is the purpose of the a=b; parts of URL syntax which was added later in the URL's history.