I spent a lot of time building juce::ValueTree
models for PFT. Here’s a couple things I found to be helpful along the way:
Hide the ValueTree
Encapsulation is one of the first things you learn about when you study object oriented design. Hide your data and prevent consumers from breaking your invariants. Building complex models on top of juce::ValueTree
s should be approached the same way for the same reasons. This not only protects the data invariant but also the type invariant.
In the past I’d build my models as plain juce::ValueTree
s. In a raw juce::ValueTree
if you want to assign a `juce::String` to a property that contains a float you can. These kinds of bugs are super irritating to figure out. Hiding your juce::ValueTree
s prevents the data they hold from becoming garbage.
Pass Value Trees around as Value Trees
juce::ValueTree
s are reference counted objects. This means that when you pass them around the shared object that the data is actually stored in remains the same. This is super convenient for passing them around when constructing your UI. It greatly simplifies data access between layers of your component hierarchy. That being said once you reach the final consumer you really should rewrap the tree to protect the invariant. I’ll typically assert in my model wrapper or listener classes to make sure I’m receiving exactly the type of tree I expect to see.
Pimpl your listeners
Sticking the juce::ValueTree::Listener
implementation into a Pimpl
class makes your model listeners a lot more useful. It means you can combine multiple model listeners without running into diamond inheritance problems.
The basic concept is:

The Pimpl
class for MyModelListener
translates juce::ValueTree::Listener
callbacks into IMyModelListener
callbacks. I usually combine the interface and the implementation in my listener classes which I’m sure is technically bad design but whatever.