Skip to content

Detecting Relationships Between Entities

Adam Mahmood edited this page Oct 2, 2018 · 2 revisions

The Problem

Currently, we don't have a mechanism or standardised approach to detect and handle broken relationships between entities.

The outcome of this spike is to outline our existing approach and consider the available strategies we could adopt.

Existing Relationships

Database ERD

GraphQL ERD

Questionnaire Content Tree

We have a simple one-to-many relationships tree for questionnaire content.

                Questionnaires
                      /\
                     /  \
                    /    \
              Sections  Sections
                 /\        /\
                /  \      /  \
               /    \    /    \  
           Pages  Pages Pages  Pages
             /\     /\    /\     /\   
            /  \   /  \  /  \   /  \
       Answers Answers  Answers Answers
          /\    /\    /\    /\    /\
   Options Options Options Options Options

What happens when you delete an entity from this tree?

  • Child nodes are also deleted

Related Questionnaire Content Trees

Some of our content trees become related through piping or routing.

This creates complex scenarios when deleting certain nodes of data.

        Questionnaire 1                 
              /\  
             /  \
            /    \
     Section 1  Section 2
          /        |
        ...        | [Routing Rule]
        /          |
   Option 1 --------

What happens when...

  • You delete "Section 2" -> The routing rule becomes detached and is no longer related to any other nodes
  • You delete "Option 1" -> The routing rule becomes detached and is no longer related to any other nodes
  • You delete "Routing Rule" -> The relation no longer exists
  • You delete "Section 1" -> Child nodes within the tree are deleted alongside routing rules and the relationship no longer exists

How can we detect broken relationships?

The most common approach is to utilise an ORM which would contain a map of relationships between entities.

Examples of ORM's:

How would an ORM help us?

  • We can control what to do when deleting a related entity (ignore / cascade / detach / reject)
  • We can obtain related entities via a consistent method
  • Creates an abstraction layer between the DB and Repositories which has the benefits of:
    • a consistent API irrelevant of DB type and schema
    • relationship mapping
    • performance benefits via cache / query optimisation

An another solution would be to detect relationships between schemas via Postgres' information_schema table.
This involves complex querying / parsing to obtain the relationships which an ORM would provide for free.

How can GraphQL help us detect relationships?

Our current schema only allows us to explore child relationships. This isn't very useful when trying to explore non-child/parent relationships as all entities are not necessarily connected in our schema.

The Relay Cursor Connections Specification provides a solution to this problem.

This would result in each entity in our schema being structured with the following:

  • Node -> represents an entity. In a diagram of circles connected by lines, these would be the circles.
  • Edge -> connects two nodes together, may include metadata. In the diagram, these would be the lines.
  • Connection -> a page-able list of nodes. In the diagram, this would be a collection of lines.

In our context the schema would look the following:

type Questionnaire {
  id: ID!
  title: String
  description: String
  sectionsConnection(
    first: Int,
    after: String,
    last: Int,
    before: String
  ): SectionsConnection
  ...
}

type SectionsConnection {
  edges: SectionsEdge
}

type SectionsEdge {
  cursor: String!
  node: SectionNode
}

type SectionNode implements Node {
  id: ID!
  name: String
  title: String
  answersConnection(
      first: Int,
      after: String,
      last: Int,
      before: String
    ): AnswersConnection
  ...
}

interface Node {
  id: ID!
  name: String
}

type AnswersConnection {
  edges: AnswersEdge
}

type AnswerEdge {
  cursor: String!
  node: AnswerNode
}

type AnswerNode implements Node {
  id: ID!
  name: String
  title: String
  routingConnection(...): RoutingConnection
  pipingConnection(...): PipingConnection
  ...
}

type RoutingConnection {
  edges: RoutingEdge
}

type RoutingEdge {
  cursor: String!
  node: Routing
}

type RoutingNode implements Node {
  id: ID!
  answersConnection(...): AnswersConnection  
  ...
}

type PipingConnection {
  ...
}

type PipingEdge {
  ...
}

type PipingNode implements Node {
  ...
}

Pros:

  • As all data is connected, relationships can be determined by exploring the connections
  • Future proofs for pagination
  • Ability to add metadata on each connection and edge
  • Common pattern used by Facebook and GitHub

Cons:

Although, Apollo is un-opinionated about this method, it does not provide any tooling to help resolve these types of schemas which:

  • Adds complexity to schema and resolvers
  • Time intensive to implement edges / connections for each node

More info: https://blog.apollographql.com/explaining-graphql-connections-c48b7c3d6976

Clone this wiki locally