Create a tree of properties?
Leverage the collection

Collections in 2.8 have a hierarchy, well at least a children collection.


Result of running test script


Can add custom properties to the collection.
Collections do not have a parent property, since they do not follow
the single parent rule.  Have added one.
Tagged the collection as "node" by adding an instance only custom
property node["node"] 
Set the root node parent to scene collection for visibility sake,
similarly if the parent attribute is set to None


Test script.

import bpy
from bpy.props import FloatProperty, PointerProperty

# clean to test
for c in bpy.data.collections:
    if "node" in c.keys():
        bpy.data.collections.remove(c)


def link_children(self, context):
    if self.parent == self:
        self.parent = None
    if self.parent:
        print(self.name in self.parent.children)
        if self.name not in self.parent.children:
            self.parent.children.link(self)
    else:
        self.parent = context.scene.collection

    mynodes = [n for n in bpy.data.collections if "node" in n.keys()]
    for n in mynodes:

        for c in n.children:
            if c.parent is not n:
                n.children.unlink(c)
        # TODO remove any "dead roots" in scene collection 
    return None

float_prop = FloatProperty()
bpy.types.Collection.float_prop = float_prop
bpy.types.Collection.parent = PointerProperty(type=bpy.types.Collection, update=link_children)

def mynode(name, scene=bpy.context.scene):
    node = bpy.data.collections.new(name)
    node["node"] = True
    return node


scene = bpy.context.scene
my_root = mynode("My Root")
my_root.parent = scene.collection
my_root.float_prop = 1

for i in range(2):
    my_trunk = mynode(f"My Trunk {i}")
    my_trunk.float_prop = 2
    my_trunk.parent = my_root
    for j in range(2):
        my_leaf = mynode(f"My Leaf {i}.{j}")
        my_leaf.float_prop = 3
        my_leaf.parent = my_trunk    


def draw_node(self, context):
    col = context.collection
    if col and "node" in col.keys():
        layout.prop(col, "float_prop")


Drawing in the UI.

Unfortunately the outliner doesn't currently have a way to display properties.  Can draw in UI with something like this, here I've used the context.collection and if it is a "node" display the float property

def draw_node(self, context):
    col = context.collection
    if col and "node" in col.keys():
        layout.prop(col, "float_prop")


Extending:

Using old fashioned properties, or getters setters with bpy.props can extend this to have what appears to be its own API, for example


Creating an API with a similar feel

from collections.abc import Mapping
import bpy

class MyTreeNodes(Mapping):
    @property
    def _storage(self):
        return {n.name : MyTreeItem(n) for n in bpy.data.collections if "node" in n.keys()}

    def new(self, name):
        node = bpy.data.collections.new(name)
        node["node"] = True
        node.parent = self.id_data.collection
        return MyTreeItem(node)


    def __init__(self,  scene, *args, **kw):
        self.id_data = scene
        self._liststorage = list()
        #self._storage = dict(*args, **kw)

    def __getitem__(self, key):
        if isinstance(key, int):
            return list(self._storage.values())[key]
        return self._storage[key]

    def __iter__(self):
        return iter(self._storage)

    def __len__(self):
        return len(self._storage)

class MyTreeItem(bpy.types.Collection):
    def insert(self, name):
        pass

    def __init__(self, coll):
        self.is_node = "node" in self.keys()


def getnodes(self):
    return MyTreeNodes(self.id_data)

class NodeTree(bpy.types.PropertyGroup):
    nodes = property(getnodes)

bpy.utils.register_class(NodeTree)    
bpy.types.Scene.mytree = bpy.props.PointerProperty(type=NodeTree)


Code below pasted into python console, shown in image above

coll = C.collection
coll
mytree = C.scene.mytree
mytree
nodes = mytree.nodes
foo = nodes.new("foobar")
foo.parent = coll
nodes[0]
nodes['