· 7 min · #unity #architecture #dependency-graph
Bloom & Place devlog #10: layers and the dependency graph
Where every system sits, what depends on what, and the before/after picture of what VContainer plus events actually changes.
Layers first, then arrows
The architecture has five layers. Each layer has an expected Instability range. Dependencies flow downward only. Top layers depend on lower layers, never the other way. This is the Stable Dependency Principle wired into the architecture itself, not enforced by hope.
Two notes on reading this:
- The I ranges are targets, not laws. A Manager class that briefly sits at 0.78 isn’t an emergency. A Core class that sits at 0.65 is.
- The arrow direction matters more than the layer names. “View” or “Manager” are descriptive. The actual rule is the arrow. Anything pointing the wrong way (e.g. Core → Manager) is a violation, regardless of what we call the boxes.
Before VContainer: the relationships I want to kill
Prototype as it stands today. Every line below is a concrete dependency one class has on another by name.
- Plantdepends onSeat · PlantPreference · PlantOutlineHover
- Plantdepends onPlantSelectionManager · CustomCursorManager↑ wrong direction
- PlantSelectionManagerdepends onPlant↻ cycle with Plant
- PlantSelectionManagerdepends onSeat · ScoreManager
- Seatdepends onPlantSelectionManager↑ SDP violation
- ScoreManagerdepends onPlant · Seat · PreferenceChecker
After VContainer: the new shape
Plant no longer references manager classes by name. It exposes the IPlantSelectable interface, publishes events, and the managers subscribe. The cycle is gone. Seat no longer reaches up.
- Plantdepends onSeat · PlantPreference · PlantOutlineHoverdata + core only
- PlantpublishesOnPlantClicked · OnPlantSelectedno compile-time link
- PlantSelectionManagerDIIPlantSelectable · ISeat · ISatisfactionSystem
- SatisfactionSystemDIIPlacementSystem · ICharacterSystem
- CustomCursorManagersubscribesOnPlantSelected
- Seatdepends onPlantdata only
Per-system Ce comparison
The numeric version of the same picture, just for the four problem children:
Plant
target Ce 2–3Before · Ce 5
Seat · PlantPreference · PlantOutlineHover · PlantSelectionManager · CustomCursorManager
After · Ce 3
Seat · PlantPreference · PlantOutlineHover (via interface)
PlantSelectionManager
target Ce 3 · interfacesBefore · Ce 3
Plant · Seat · ScoreManager
After · Ce 3
IPlantSelectable · ISeat · ISatisfactionSystem
SatisfactionSystem
target Ce 2Before · Ce 3
Seat · Plant · PreferenceChecker
After · Ce 2
IPlacementSystem · ICharacterSystem (DI-injected)
Seat
target Ce 1Before · Ce 2
Plant · PlantSelectionManager
After · Ce 1
Plant (data only)
The interface trick on PlantSelectionManager is the one most worth highlighting. The Ce number doesn’t drop, but it changes from “depends on 3 concrete classes I can’t replace” to “depends on 3 interfaces I can swap or fake.” Same number, different kind of coupling. Constantine’s metric doesn’t capture that distinction; SDP does, and the verdict column on the next measurement pass should reflect it.
What’s removed in plain terms
The two specific dependencies the refactor kills:
Plant→PlantSelectionManager. Was insidePlant.OnClick()callingPlantSelectionManager.Instance. Becomes:Plantraises anOnPlantClickedevent, andPlantSelectionManagersubscribes.Plant→CustomCursorManager. Was insidePlant.SetSelected()callingCustomCursorManager.Instance. Becomes:Plantraises anOnPlantSelectedevent, andCustomCursorManagersubscribes.
Both edits compile to the same runtime behaviour. The only thing that changes is the direction of knowledge. After the edit, Plant no longer knows that PlantSelectionManager or CustomCursorManager exist.
Next
Devlog #11: how the systems are registered with VContainer (binding scheme + lifetimes), and the seven events that wire them together at runtime (event flow).