The Layered Dependency View is one of the reports in Turbo EA. It stacks the four architecture layers as horizontal lanes, with Strategy and Transformation at the top and Technical Architecture at the bottom, places every card in its lane and draws every relation between cards as a line.
A landscape of a few hundred applications, capabilities and IT components produces several hundred such lines. Drawn naively, from the centre of one card to the centre of another, they cross each other, run through cards and merge into bundles nobody can follow. This article explains what the view does instead: two phases of small, well-known passes that move cards first and draw lines last. Most of the work happens before a single line is drawn.
The idea
The view follows the Sugiyama method for drawing layered graphs, the same family DrawIO’s hierarchical layout belongs to. It works in two phases. Placement decides where each card sits and removes most crossings while moving a card is still cheap. Routing then draws whatever is left with right angles, under two rules it never breaks: two lines don’t share a path, and lines leaving the same side of a card don’t cross at its border.
Phase 1: placement
Placement only moves cards. Each pass treats lines as straight segments between card centres.
1Lanes
The four EA layers are stacked top to bottom in a fixed order: Strategy & Transformation, Business Architecture, Application & Data, Technical Architecture. Almost every relation therefore runs vertically between lanes, and a reader always scans in one direction. Inside a lane, the dagre layout library ranks cards top to bottom using the relations within that lane, with 90 px between ranks and 50 px between cards. A lane with no internal relations becomes a grid of at most three columns.
2Median alignment
Each lane is laid out without knowing about relations to other lanes, so two linked cards in adjacent lanes can land far apart. A second pass sweeps the lanes down, up and down again, moving each card toward the median x of its neighbours. On each sweep only the lanes already visited pull on a card, which stops cards from oscillating. Rows stay fixed and only x changes.
3Hubs first
When several cards in a row want the same spot, their left-to-right order comes from where each wants to be, but they are positioned in descending order of connection count. The most connected card takes its exact column, and cards with fewer links move aside by the minimum gap. The busiest cards, whose lines matter most, get straight verticals.
4Transpose
Two neighbouring cards in a row swap places whenever that strictly reduces the number of crossings among their lines. Lines above and below the row are counted separately, because they can’t cross each other. Every accepted swap lowers the total, so the pass always finishes.
Phase 2: routing
Routing works on fixed positions. For every line it picks which of the card’s 24 handles to use and where the bends go. It never moves a card.
1Classify
A relation between two cards in the same lane that is mostly horizontal (|dx| > 2.5 × |dy|) uses the facing side handles and becomes a short straight line. Only the first relation between a pair gets this, because each side has a single handle point and a second line would be drawn on top of the first. Everything else leaves through the top or bottom of the card.
2Ordered attachment points
The top and bottom of each card carry five attachment points, at 12, 30, 50, 70 and 88% of its width. For each side, the lines attached there are sorted by the direction of their other end (horizontal distance divided by vertical distance), so steep lines take the middle points and shallow ones fan out. A small dynamic program then assigns points in that order, keeping them strictly left to right while putting each as close as possible to where its partner is. A partner directly below gets the aligned point and a dead-straight line.
Because the order is fixed first, two lines leaving the same side can never cross at the border. Lines leaving downward and lines arriving from below share the bottom border, so both kinds are ordered together.
3One corridor for long lines
A relation with whole rows of cards between its two ends would zig-zag if it dodged each row separately. Instead the router turns every crossed row into blocked intervals (each card plus 12 px clearance, plus corridors other lines have already taken) and looks for one x that is free through all of them. It tries straight into the target, then straight out of the source, then the free x nearest the target.
Each end then joins the corridor with as few bends as possible: straight (0 bends), through the side of the card when the corridor runs right beside it (1), or with a jog in the gap between rows (2). The corridor is then reserved, so the next long line keeps 12 px away from it.
4Separate tracks for horizontal segments
A standard right-angled path puts its horizontal segment halfway between its two ends. Every line between the same two rows would then run along the same y and merge into one wire. The router gives each line its own y instead. Horizontal segments that overlap in x are grouped. Within a group, segments that don’t overlap share a track, and overlapping ones get separate tracks 16 px apart, centred in the gap. This is the track assignment used in chip wiring.
Each track is then nudged off any card or lane header it would cross. If a line’s path still cuts through a card, it falls back to a wrap-around shape.
5Nudge overlapping verticals
Lining cards up in columns has a side effect. Two vertical segments from different cards can land on the same x over the same height and look like one line. When two verticals are within 6 px of each other and overlap by more than 12 px, the router moves one end to the neighbouring free attachment point. It only does so if the move keeps the order from step 2.
6Labels
Labels that would land within 80 × 22 px of each other are spread along their own lines, between 20% and 80% of the way, in left-to-right order. A label that would sit on a card slides toward the middle of its line.
On screen, every path gets 8 px rounded corners. Each route remembers where its ends were at layout time. If a card is dragged, the stored bends no longer fit, and the view draws a plain right-angled path for that line instead of a broken one. The Create diagram action carries the same bends into DrawIO.
Why it works
- Fix crossings where it’s cheap. Moving a card early removes many crossings at once. Dodging them during routing adds bends.
- Crossings at a card’s border can’t happen. The ordered attachment points rule them out by construction.
- Two lines never share a path. Horizontal segments get separate tracks, verticals get nudged apart, long lines get reserved corridors, and two relation types between the same pair of cards get separate points.
- Few bends. A long line uses one corridor, and each end joins it with at most two bends.
- The same graph always draws the same way. Every tie is broken by id or position, so a reader’s mental map survives a reload.
Limits
- It uses heuristics. Nothing computes the minimum possible number of crossings. The crossing counter in the code base exists only for the tests.
- Five points per side. A card with more than five lines on one side shares points, so dense hubs can still bundle.
- Drags drop the stored route. A moved card gets plain right-angled paths, so heavy rearranging can bring tangles back.
- Label placement is approximate. It is worked out along the straight line between the two ends, not along the bends.
- Corridors only for whole rows. A line is corridor-routed only when complete rows of cards sit between its ends. Other obstructions use the wrap-around fallback.
Comments
Have you found a landscape the view still tangles, or a layout rule it should add? Share what you see.