bNodeTree is currently both a datablock, and embedded in materials. Same for collection and master collection of a scene.
This leads to messy datablock management and problems creating RNA paths (e.g. T68971).
A solution to this could be to make bNodeTree node longer a datablock. Instead it would be embedded in Material, World, Light, and a new NodeGroup datablock.
This will require Python API breakage and some relatively complicated versioning, but the resulting design should be cleaner.
Another solution is to design a proper handling of those private data-blocks all around our code base (ID/Lib management in BKE_library area, RNA handling of IDs, etc.). We already do that to some point, but so far it has mostly been hacks around issues on a case-by-case basis.
Solution to T68971 already added a new ID flag to mark those private datablocks, e.g. we could extend RNA handling of IDs to detect that one and automatically consider those structs as the other private sub-data ones (like e.g. render settings from scene, etc.).