Clarification on Resources Composed of Other Resources

I have been working with JSON API for the past 9 months and have been under the assumption after reading the specification several times that Resources can be composed of other Resources when the relationship is composition or whole/part type of containment between Resources. Not that the specification directly said this but never directly said it could not and composition is so fundamental I just assumed composition was legal.

Let me give you a real world example: Here at work is an example JSON API document that represents a layout for a theater auditorium. A layout is composed of sections and seats for this example and I removed a lot of content just to show the composition intent:

    {
      "links": {
        "up": "http://localhost:8040/v2/en-us/layouts",
        "self": "http://localhost:8040/v2/en-us/layouts/1190874"
      },
      "data": {
        "type": "layouts",
        "id": "1190874",
        "attributes": {
          "name" : "Auditorium 1",
          "sections": [
            {
              "type": "sections",
              "id": "1190874-11-47-0",
              "attributes": {
                "name": "Reserved Seating",
                "tags": [],
                "version": null
              },
              "relationships": {
                "resource-seating-type": {
                  "links": null,
                  "data": {
                    "type": "seating-types",
                    "id": "SelectASeat"
                  }
                },
                "collection-seat": {
                  "links": {
                    "related": "http://localhost:8040/v2/en-us/layouts/1190874/sections/1190874-11-47-0/seats"
                  },
                  "data": []
                }
              },
              "links": {
                "self": "http://localhost:8040/v2/en-us/layouts/1190874/sections/1190874-11-47-0"
              }
            }
          ],
          "zones": [],
          "seats": [],
          "version": null
        },
        "relationships": {
          "collection-section": {
            "links": {
              "related": "http://localhost:8040/v2/en-us/layouts/1190874/sections"
            },
            "data": [
              {
                "type": "sections",
                "id": "1190874-11-47-0"
              }
            ]
          },
          "collection-zone": {
            "links": {
              "related": "http://localhost:8040/v2/en-us/layouts/1190874/zones"
            },
            "data": []
          },
          "collection-seat": {
            "links": {
              "related": "http://localhost:8040/v2/en-us/layouts/1190874/seats"
            },
            "data": []
          }
        },
        "links": {
          "self": "http://localhost:8040/v2/en-us/layouts/1190874"
        }
      },
      "included": [
        {
          "type": "seating-types",
          "id": "SelectASeat",
          "attributes": {
            "name": "Select-A-Seat",
            "isSeatSelectable": true
          },
          "relationships": null,
          "links": {
            "self": "http://localhost:8040/v2/en-us/seating-types/SelectASeat"
          }
        }
      ]
    }

But after reading the following from the specification I believe that composition is not legal because Resources in the Attributes section of another Resource contains Relationships and Links which seems to be illegal based on the following part of the specification talking about the attributes object of a Resource.

Complex data structures involving JSON objects and arrays are allowed as attribute values. However, any object that constitutes or is contained in an attribute MUST NOT contain a relationships or links member, as those members are reserved by this specification for future use.

Is this really the case that Resources can not be composed of other Resources or am I misinterpreting the part of the specification talking about the attributes object of a Resource.

This is the way I have been reading the spec;

Resources should be related via the relationships attribute. For your case I would be moving all the embedded resources out of the attributes element and into the relationships keeping only the links and data. To “embed” these resources have them in your included section. Again the resources in included should not embed other resources but rather link to them. In the end your document should hold all the resources you want to return and should be linked to one another.

In the below example the primary data is a layout which contains a relationship to a section. The section is “embedded” in the document in the includes section. The section object also has a relationship to a seating-type. That resource is also included in the included section. These are all brought together through their linkage. Non of the related resources are embedded inside the resource itself but rather linked together and made available through their inclusion in the document.

{
  data:
  {
    "id":"1190874",
    "type":"layout",
    "attributes":
    {
      "name":"Auditorium 1",
      "version":null
    },
    "relationships":
    {
      "sections":
      {
        "data":
        [
          {"id":"123", "type":"section"}
        ]
      }
    }
  },
  "included":
  [
    {
      "type":"section",
      "id":"123",
      "attributes":
      {
        "something":"whatever"
      },
      "relationships":
      {
        "seating":
        {
          "type":"seating-type",
          "id":"1"
        }
      }
    },
    {
      "type":"seating-type",
      "id":"1",
      "attributes":
      {
        "description":"Best seats in the house"
      }
    }
  ]
}
1 Like

Hi @adam, thanks for the feedback. I get exactly what you have said and your json:api document is exactly what the layout resource would have to be if resource composition is not allowed as that part was easy. I just assumed resource composition was allowed if two resources are associated in a whole/part type of relationship, i.e. the composite resource has the responsibility for the existence and storage of the composed resources (parts).

With that said and after pondering this topic for the past couple of days, I don’t mind refactoring my work so composed resources (parts) are automatically placed in the included section either by the server or manually requested by the client via an “include” as that would actually simplify some of the framework code developed to date. The drawback is you lose the graph structure of composed resources when it is true composition between resources but you gain some simplifications such as the resource hierarchy is truly flattened with no possibility of duplication (normalization), resource linkage is simplified as I no longer have to worry about resource containing other resources and thus recursively examining resources, and finally makes resource design decisions easy as there is no decisions to make - resources can not contain other resources…

You guys have basically nailed this issue in your discussion… @adam’s correct about the spec and @scottmcdonald’s got a lot of the reasoning behind it.

Note that there has been a (longstanding) discussion about making composition possible when the sub resource really is fully contained. Some of that discussion is in #383, and I’ve argued in more recent issues that allowing this could the best solution to some limitations around the "type" key, but this hasn’t happened yet!