If there is a 1-to-1 relation between a web form and a table in the database, everything is nice and easy.

Even if you have a web form which results in creating e.g. a User and Account record, everything is still nice and easy.

Complexity creeps in when a web form starts having nested, repeatable fields or just a lot of fields.

As an example let’s imagine having the concept of a Document for which the associated data could be modelled as such:

{
  "title": "Document Title",
  "author_id": 1,
  "parts": [
    {
      "content_type": "text",
      "body": "Just some text in the beginning"
    },
    {
      "content_type": "image",
      "image_url": "http://example.com/some-image.png"
    },
    {
      "content_type": "text",
      "body": "... and some text at the end."
    }
  ]
}

This could be modelled as a Document model with many DocumentParts.

The complex method

The approach I would have taken a few years back would be to think that a Document cannot exist in a not-done state. This would require having a UI where you can add/remove parts, usually handled with client-side JS. The client-side JS quickly becomes complex because you would have to handle a lot client-side related to the content_type parameter in this case.

The simple method

Instead a lot could be simplified by first letting the user fill in the title and then immediately persist a Document record. Now we would let the user add the parts one-by-one and this could be simplified even further by making the user select the content_type beforehand and presenting a different UI depending on that parameter.

For this method it would probably also make sense to track whether the Document is done or being worked on with for instance a draft flag. When the user clicks Save after building the initial document we would set draft=false.

A big win is also that validation becomes very easy because you’re no longer validation a complex nested model but usually just a single model at a time.

Conclusion

Why is the simple method simpler? Because once a record is persisted in the database we can leverage the power of having endpoints, e.g. /documents/1/parts/image/new. Adding and deleting a DocumentPart would also no longer require you to track whether it is a new or existing part as it will be persisted, so you just DELETE /documents/1/parts/2 if the user wants to delete a part.

One slight disadvantage of “The simple method” is that for API integration you would like to make a single request to create the Document + DocumentParts, so reusing controllers could become tricky. Nonetheless “The simple method” has still a lot of advantages.