It depends on what the goal is. If you want external clients (such as smartphone apps) to access the plugin, then the REST API is the way forward. OctoPrint's server should never call its own REST API from the Python side, if you need plugin-to-plugin communication on the server side then helpers are the way to go:
Depending on your provided data you can also "push" the data to other plugins.
So, instead that the 3rd party plugin needs to activly read/pull the data, you can fire an event with a payload thru the eventbus. Other plugins can then listen for such event.
Bed Level Visualizer plugin has the event registration as example of the current methods here and fired here. It also has examples of REST API and the use of send_plugin_message which is another way that other plugins can respond to information on the UI side.
IMHO you shouldn't implement stuff that is not needed, because each time you need to change something in the viewModel you need to make sure that the other ("unknown") consumers can handle the change. (Synced-Releases or backward compatibility)
In the past someone used (without asking) a viewmodel from one of my plugins, I did some refactoring (renaming, fixing typos,.. ) ..and a lot people were upset, because the other plugin was broken.
Yeah, I think the websocket approach for other plugins to get shared data makes the most sense. I've only ever used other plugin viewModels js functions to verify if the plugin was installed and enabled in order to react differently in the UI.
Work finished...if somebody like to comment the documentation feel free. I would be happy to get feedback. Decided that every plugin should implement its view interface which would avoid issues with undocumented calls.