If most operations require data joins then pick a de-normalized structure. Then most data is often available with a single hash lookup. Though this makes updates difficult and mistakes can and often do lead to inconsistent data.
A normalized data-structure which use ids to refer to related elements makes it easy to add, update, and delete entities; but reads might require multiple joins which in turn makes the code complex.
We can choose between the two by listing out the possible operations and deciding which cases are more frequent. But this is often a moving target in a growing application.
The best approach I've so far found is to design data structures in a way that "invalid states are impossible". I will not link to Yaron Minsky and Richard Feldman's excellent talks on this here. This principle often results in elegant data structures that would've eluded me otherwise.
The second addition that is necessary is to use a statically typed language. Elm, Reason, and PureScript are the only choices in front-end at the moment because of soundness, sum/union types, and exhaustive pattern matching. A typed functional language makes refactoring an easy, mechanical, and reliable process which suddenly makes our code much more malleable and hospitable than before.