Skip to content
Erik Huelsmann edited this page Apr 18, 2020 · 16 revisions

This page serves as a discussion board for the various API definitions we're considering and the pros and cons of each. The first discussion point is the GL transaction resource.

GL Transaction resource

REST (non-JSONAPI)

A GL transaction API, when modelled as a REST resource might look like (without HATEOAS):

{
   "id": 9,
   "date": "...",
   "description": "...",
   "notes": "...",
   "lines": [
      { "account": { "accno": "...", "description": "...", "id": 12 },
        "amount": 23.2, "curr": "EUR",  },
      { "account": { "accno": "...", "description": "...", "id": 11 },
        "amount": -23.2, "curr": "EUR",  },
   ],
   "created": {
     "date": "...",
     "by": "...",
   },
   "approved": {  }

REST (JSONAPI)

The same resource could look like:

{
   "data": {
      "id": 9,
      "type": "gl-transaction",
      "attributes": {
         "date": "...",
         "description": "...",
         "notes": "...",
         "lines": [
            { "account": { "accno": "...", "description": "...", "id": 12 },
              "amount": 23.2, "curr": "EUR",  },
            { "account": { "accno": "...", "description": "...", "id": 11 },
              "amount": -23.2, "curr": "EUR",  },
         ],
      }
   },
   "meta": {
      "created": {
         "date": "...",
         "by": "...",
      },
      "approved": {  }
   }
}

Note that this design similarly doesn't have links to other resources (HATEOAS). There are a few other things to note about it:

  • JSONAPI only defines how to change 'toplevel' attributes and relationships, meaning that lines will need to be edited as a whole;
  • the account object in each line contains unmodifiable data, except for the id
  • since the account object isn't a toplevel field, it's not an option to tranform it into a relationship, meaning it's not possible to link the to an external resource

Invoice resource

Applying the same reasoning that no external resource links are required for invoices, the resource might look like the ones below

REST (non-JSONAPI)

{
   "id": 9,
   "customer": {
      "id": 1200,
      "name": "ABC",
   },
   "date": "...",
   "description": "...",
   "notes": "...",
   "lines": [
      { "invoice": { "part": { "id": 99, "onhand": 999, "price": 5.00 } },
        "gl":      { "account": { "accno": "...", "description": "...",
                     "id": 12 }, "amount": 23.2, "curr": "EUR",  } },
      { "gl":      { "account": { "accno": "...", "description": "...",
                     "id": 11 }, "amount": -23.2, "curr": "EUR",  } },
   ],
   "created": {
     "date": "...",
     "by": "...",
   },
   "approved": {  }

REST (JSONAPI)

REST (JSONAPI) [try 1]

{
   "data": {
      "type": "invoice",
      "id": 9,
      "relationships": {
         "customer": { "data": { "id": 1200, "type": "customer" } }
      },
      "attributes": {
         "date": "...",
         "description": "...",
         "notes": "...",
         "lines": [
            { "invoice": { "part": { "id": 99, "onhand": 999, "price": 5.00 } },
              "gl":      { "account": { "accno": "...", "description": "...",
                           "id": 12 }, "amount": 23.2, "curr": "EUR",  } },
           { "gl"  :     { "account": { "accno": "...", "description": "...",
                           "id": 11 }, "amount": -23.2, "curr": "EUR",  } },
         ],
      },
   "meta": {
      "created": {
        "date": "...",
        "by": "...",
      },
      "approved": {  }
   }
}

The above definitions are off though: the onhand field shouldn't be part of the invoice. The field is part of the informational data provided to the user during the invoice-entry process, but the value is really associated with the part itself.

A more natural design would therefore be that the part be relationship data of sorts, which would allow the JSONAPI server to return the related part data (and thus on hand data) with the request, instead of "in" the invoice data. Complicating matter to do this, is that the JSONAPI doesn't define a way to reference resources other than on top-level fields; yet the 'part' data in the invoice isn't a top-level field.

REST (JSONAPI) [try 2]

The problem described above - the fact that the parts data isn't top-level data - can be solved by making each invoice line a separate sub-resource of the invoice, linking to those from the invoice, and linking to the parts from those.

This solution feels wrong: an invoice is the collection of its lines. The collection of lines is the defining property of the invoice. To that extent, it seems wrong to define the lines as separate sub-resouces. (To that extent, the lines of an invoice are very much something different to an invoice than addresses to a customer -- the latter being something that I can see a sub-resource.)

Below there is a "solution" to this problem: the server generates a read-only surrogate field invoice-parts which does contain the relationships with the parts used in the invoice. The existence of this relationship will allow the server to include parts data (and therefore onhand numbers).

{
   "data": {
      "type": "invoice",
      "id": 9,
      "relationships": {
         "customer": { "data": { "id": 1200, "type": "customer" } },
         "invoice-parts": [
             { "data": { "id": 99, "type": "part" } },
         ],
      },
      "attributes": {
         "date": "...",
         "description": "...",
         "notes": "...",
         "lines": [
            { "invoice": { "part": { "id": 99, "price": 5.00 } },
              "gl":      { "account": { "accno": "...", "description": "...",
                           "id": 12 }, "amount": 23.2, "curr": "EUR",  } },
           { "gl"  :     { "account": { "accno": "...", "description": "...",
                           "id": 11 }, "amount": -23.2, "curr": "EUR",  } },
         ],
      },
   "meta": {
      "created": {
        "date": "...",
        "by": "...",
      },
      "approved": {  }
   },
   "included": [
      { "data": { "type": "part", "id": 99, "onhand": 999 } },
   ]
}