Relationships with data: how to manage links and data in the related resources

Hi!
Again a question about relationships with data and the related resources.

I’m having some troubles to find a solution for this problem. We have:

  • projects
    Projects exists on their own, hence we have defined the following endpoints:
    • projects
    • projects/{projectId}
  • templates
    Templates exists only within a project. Also we have the following endpoints:
    • projects/{projectId}/templates
    • projects/{projectId}/templates/{templateId}
  • components
    Components are also independent entities hence we have:
    • components
    • components/{componentId}

Each component could be assigned to one or more projects and, at the same time, be referenced within one or more templates. Therefore we have two n:n relationships:

  • one between project and component
  • the other between template and component

The relationships carry (for now) the position of the component within the list of the components (for project and template they could be different). To model this we have therefore defined the following fictional resources:

  • templateComponents
    This resource carries the position of a component within a template. For this we have:
    • /projects/{projectId}/templates/{templateId}/templateComponents
    • /projects/{projectId}/templates/{templateId}/templateComponents/{templateComponentId}
  • projectComponents
    This resource is created to carry the position of a component within a project. For this we have:
    • /projects/{projectId}/projectComponents
    • /projects/{projectId}/projectComponents/{projectComponentId}

Having real endpoint for the relationships allows to add/remove and modify the content of them without having to interact with the whole basic resource.

After this premises we could think about the structure of the data shared by each endpoint. To simplify the things, given that project<->component and template<->component are almost the same from now we focus just on the second one.
Also, without concentrating on templateComponent part, our initial drafts are:
Project

{
   "data":{
      "id":"projectA",
      "type":"project",
      "attributes":{
         "name":"Project A"
      },
      "relationships":{
         "templates":{
            "links":{
               "related":"/project/projectA/templates/template1_A"
            },
            "data":[
               {
                  "id":"template1_A",
                  "type":"template"
               },
               {
                  "id":"template2_A",
                  "type":"template"
               }
            ]
         }
      }
   }
}

Template

{
   "data":{
      "id":"template1_A",
      "type":"template",
      "attributes":{
         "name":"Template 1 for project A"
      },
      "relationships":{
         "projects":{
            "links":{
               "related":"/projects/projectA"
            },
            "data":[
               {
                  "id":"projectA",
                  "type":"project"
               }
            ]
         }
      }
   }
}

and Component

{
   "data":{
      "id":"componentX",
      "type":"component",
      "attributes":{
         "name":"My first component"
      }
   }
}

TemplateComponent requires a bit more thinking. The resource itself is just a join table with data between other two tables. It even doesn’t have its own key, but it compose the ones of template and component. Given that the endpoint is below project and template and we could always unambiguously identify one part of the composite key (the templateId), to relief the problem to manage a virtual key we opted to model the resource using the id of the component. Hence we have:
templateComponent

{
   "data":{
      "id":"componentX",
      "type":"templateComponent",
      "attributes":{
         "position":"1"
      },
      "relationships":{
         "template":{
            "links":{
               "related":"/project/project1/templates/templateA_1"
            },
            "data":[
               {
                  "id":"templateA_1",
                  "type":"template"
               }
            ]
         },
         "component":{
            "links":{
               "related":"/components/componentX"
            },
            "data":[
               {
                  "id":"componentX",
                  "type":"component"
               }
            ]
         }
      }
   }
}

This works good for managing the resource.
Now we just have to adapt template and component to integrate this new information. Also, for template the things went really smooth. We extend relationships with the following and everything is working.

{
   "data":{
      "id":"template1_A",
      // data omitted
      "relationships":{
      // data omitted
         "templateComponents":{
            "links":{
               "self":"/projects/projectA/templates/template1_A/relationships/templateComponents",
               "related":"/projects/projectA/templates/template1_A/templateComponents"
            },
            "data":[
               {
                  "id":"componentX",
                  "type":"templateComponent"
               }
            ]
         }
      }
   }
}

On the other side, for components we are faced with some troubles. We can’t just add another field in the relationships field, because each component could be references by several templates. Therefore something like this is not working:

{
   "data":{
      "id":"componentX",
      "type":"component",
      "attributes":{
         "name":"My first component"
      }
   },
   "relationships":{
      "templateComponents":{
         "links":{
            "self":"/projects/projectA/templates/[templateId???]/relationships/templateComponents/componentX",
            "related":"/projects/projectA/templates/[templateId???]/templateComponents/componentX"
         },
         "data":[
            {
               "id":"componentX",
               "type":"templateComponent"
            }
         ]
      }
   }
}

Because the referenced template is missing.
To correctly map the templateComponent I would need an array of ids for every template where the component is used, but (from my understanding) this is not possible. I would like to have a url like /projects/projectA/templates/{templateId}/templateComponents.

Could it be that I’ve modeled the things in a wrong way? I’ve already thought about other ways to do the things but:

  • with a fictional id for templateComponent I would face it anyway
  • modifying the endpoints and map the resources in a different way I’ve no guarantees that a similar problem would arise in a different form (maybe instead of the path on the query params)

Finally, should I really map all the resources and their relationships in this way?

Could it be that for the component the relationship is just with templates? This would work, because a link with an array of data is exactly what I have and what I could map. To know more about a component within a template I’ve always to load the template and get the other data from the relationship.

What are your thoughts about this? Could someone help me, please?