Alrighty.... I violated the implementation lines of your poem, but (weakly) in my defense, most of the fun was trying to make the game engine highly concurrent and distributed, so that always adds some complexity and indirection, to ensure that threads don't mess with each other's data directly and cause nasty race conditions and concurrent modification exceptions.
At the top of the object graph was something I called the "Realm". The realm contained a bunch of areas, or "Dungeons". The realm provided methods to look up dungeons by name, returning a reference to a dungeon. It also provided methods to start, stop, create, and delete dungeons. When the realm booted, it read a directory hierarchy off disk that contained the game state and used that data to initialize the dungeons back into whatever state they were left in when the realm was last shut down.
Each Dungeon ran in its own thread, and Dungeons could not access each other's object graphs. The only globally accessable methods provided by the dungeons were methods that they could use to pass Tasks (glorified Runnables) to each other through concurrent queues. The idea here was to eventually be able to replace all of the shared memory operations above this level with stuff being serialized over TCP sockets, so that a realm could run on a cluster/cloud, or you and your buddies could link your realms together over the internet into one big game world.
Internally, the dungeons maintained a 3d grid of GameObjects in a threadlocal hashmap keyed with 3d integer coordinates. These toplevel GameObjects represented "cells" on the map. The dungeon also internally maintained a table of GameObjectData (private), keyed by UUIDs, and all internal GameObject operations looked up data on this table by that key, instead of holding direct references to other GameObjects. This bit of indirection was to enforce that dungeons could /only/ access game objects contained within themselves. This made higher level property programming simpler, because the world builder could write their properties without having to worry about narsty concurrent programming issues.
GameObjects provided methods to look up their Parent (the GameObject that contained them) and their Children (the GameObjects that they contained). They also provided methods to move themselves to a new parent. A GameObject's parent and children were not directly modifiable; you could only move them to a new parent. This was to ensure that the object graph never became circular (example: a chest containing a bag, while the bag also contained the chest, which would have caused code that walked up and down the object tree to get stuck in an endless loop).
GameObjects also provided methods to add and remove Properties from them. So if I wanted to make a wall at coordinate (0,0,0), I'd create a new GameObject, add an Obstacle property, a Graphic property, and maybe a Name("Wall") property to it, and then move the GameObject to (0,0,0).
Early implementations used a global Heartbeat event, but I later removed that as it proved to be too CPU intensive with large worlds. So Properties could register a Timer event, either repeating or one-shot, and an OnTimer event would be raised to that property at the given interval. This is how monsters wandered aimlessly around the dungeon waiting for players to run into them.
Some event handlers could return a boolean value that could signal whether the event chain was to be interrupted or not.
For example, when an object was about to move, it would raise an OnMoving event to the set of all objects near it and all objects near its destination. Any of these OnMoving event handlers could return true, in which case the event chain was canceled and the object did not move.
This was handy for this sort of use-case: A player is standing next to a wall. The player pushed a direction key, trying to move into the space occupied by the wall. The wall gets the player's OnMoving event. Seeing that the player is trying to occupy the space that the wall is in, the wall prints a message to the area saying, "So-and-so runs into the wall and falls down!". Then the wall returns true, canceling the event chain and preventing the player from moving.
(Edit: I think the key thing here regarding cleanliness, that event driven programming is really good at is.... Notice that the wall's behavior (blocking movement) is implemented /by the wall/, not by the movement code on the player. If I go to change how walls work, it's all there in one place. There is no spaghetti of stuff between the wall and the player movement code and your toaster and the neighbor's dog.)
Once an object had successfully moved, another event, OnMoved was raised to the area. Monsters used this to notice when other things wandered around the dungeon, so that they could see players coming and decide to either flee or become aggressive. Players also used this to send screen update packets to their clients.
There were other low-level events that were raised by the engine, things like properties being added or removed and such. I never used them for much, but the idea was that critters would be able to notice when spells wore off and re-cast them and stuff like that.
Higher level events (such as combat and damage events, speech, etc) were raised by other object properties rather than the low-level engine.
Since dungeons were only allowed to know about objects that they contained, the issue of moving objects between dungeons was handled by the dungeon Tasks mentioned above. An object (and its children) to be moved to another dungeon was converted into an "object blueprint" and removed from the dungeon's object table. The blueprint was then packaged up into a Task, which was enqueued to the other dungeon. The new dungeon would dequeue the Task in its own thread and run it. The run() method of the task would then rebuild the object (and its children) in the new dungeon from the blueprint. Since the actual "GameObject" was just a wrapper around a UUID, with some internal GameObjectData methods attached to it, the gameobjects that the properties of newly moved objects referred to would just resolve to nothing in the new dungeon, instead of creating a concurrency nightmare.
There was no broadcast to all dungeons with that paradigm, and objects in different dungeons had to communicate with each other asynchronously with a rather obtuse task passing system. That worked ok with a convenience layer on top of it, until the objects lost track of which dungeon the other object was in and sent the tasks to the wrong place. I never did fix this, but thought about implementing some kind of Object ID registry in the realm so that things could look up what dungeon an object with some ID was in. This would have put a crimp in the massively concurrent idea (though I suppose I could have stored it in some kind of big distributed hashtable), so I kept thinking about it and never got around to fixing it.
So, there it is. Rather impractical for a game of roguelike scope, but it sure was fun figuring out how to make it work. Lol. Not designing it to be highly concurrent would have made it vastly cleaner. A single-threaded game could have just kept direct references to other objects instead of internally referencing a data object by UUID, and could have done away with the task-passing system.