Connection lost. Reconnecting… attempt 1 of 8
Paused. Your work is held on the server.
Could not reconnect.
This session has expired on the server.
An unhandled error has occurred.
Sedna.UI
v0.31.0 · main

Graph layouts

tier 3 — a surface

Where the records go. data-graph-layout names a layout, and each answers a different question: what the whole looks like, what surrounds one record, who reports to whom, where things physically are. Pick the one whose question your reader is asking, rather than the one that looks busiest.

The same drawing every time Every layout is deterministic: records are ordered by how many links they have and then by id, and nothing is left to chance, so the drawing a reader learned yesterday is the one that comes back today. dagre and fcose are plugins, loaded the first time a graph uses them. The records, the frame and the keys are on Graph.

Force

The default: springs along the links, so what is linked sits together and the shape of the whole is the first thing a reader sees — here, which libraries everything shares and which only one service uses. Use it for a map with no hierarchy and no groups. A data-root is placed at the centre first and the springs work outwards from it. Every link here says “uses”, so data-graph-edge-labels="none" leaves the labels off.

  • Storefront
  • Checkout
  • Payments
  • Orders
  • Invoices
  • Search
  • Indexer
  • Returns
  • Order mails
  • Reports
  • http-client
  • auth-sdk
  • metrics
  • retry-policy
  • money
  • pdf-render
  • mail-templates
  • text-analyzers
  • image-resize
  • The storefront uses http-client
  • The storefront uses auth-sdk
  • The storefront uses image-resize
  • The storefront uses metrics
  • Checkout uses http-client
  • Checkout uses auth-sdk
  • Checkout uses money
  • Checkout uses retry-policy
  • Checkout uses metrics
  • Payments uses http-client
  • Payments uses money
  • Payments uses retry-policy
  • Payments uses metrics
  • Orders uses money
  • Orders uses retry-policy
  • Orders uses metrics
  • Invoices uses money
  • Invoices uses pdf-render
  • Invoices uses mail-templates
  • Returns uses money
  • Returns uses pdf-render
  • Returns uses auth-sdk
  • Order mails uses mail-templates
  • Order mails uses retry-policy
  • Search uses text-analyzers
  • Search uses metrics
  • The indexer uses text-analyzers
  • The indexer uses http-client
  • Reports uses pdf-render
  • Reports uses money
Service Shared library
<div class="graph-frame">
    <div class="graph" data-graph id="layout-force" data-graph-edge-labels="none" aria-label="Which services use which shared libraries">
        <ul class="graph-data" data-graph-data>
            <li data-node="storefront" data-kind="service" data-tone="2" data-icon="ri-store-2-line">Storefront</li>
            <li data-node="checkout" data-kind="service" data-tone="2" data-icon="ri-secure-payment-line">Checkout</li>
            <li data-node="payments" data-kind="service" data-tone="2" data-icon="ri-bank-card-line">Payments</li>
            <li data-node="orders" data-kind="service" data-tone="2" data-icon="ri-file-list-3-line">Orders</li>
            <li data-node="invoices" data-kind="service" data-tone="2" data-icon="ri-bill-line">Invoices</li>
            <li data-node="search" data-kind="service" data-tone="2" data-icon="ri-search-line">Search</li>
            <li data-node="indexer" data-kind="service" data-tone="2" data-icon="ri-refresh-line">Indexer</li>
            <li data-node="returns" data-kind="service" data-tone="2" data-icon="ri-arrow-go-back-line">Returns</li>
            <li data-node="mail" data-kind="service" data-tone="2" data-icon="ri-mail-send-line">Order mails</li>
            <li data-node="reports" data-kind="service" data-tone="2" data-icon="ri-bar-chart-box-line">Reports</li>
            <li data-node="http" data-kind="library" data-shape="square" data-tone="5">http-client</li>
            <li data-node="auth" data-kind="library" data-shape="square" data-tone="5">auth-sdk</li>
            <li data-node="metrics" data-kind="library" data-shape="square" data-tone="5">metrics</li>
            <li data-node="retry" data-kind="library" data-shape="square" data-tone="5">retry-policy</li>
            <li data-node="money" data-kind="library" data-shape="square" data-tone="5">money</li>
            <li data-node="pdf" data-kind="library" data-shape="square" data-tone="5">pdf-render</li>
            <li data-node="templates" data-kind="library" data-shape="square" data-tone="5">mail-templates</li>
            <li data-node="analyzers" data-kind="library" data-shape="square" data-tone="5">text-analyzers</li>
            <li data-node="images" data-kind="library" data-shape="square" data-tone="5">image-resize</li>
            <li data-edge data-source="storefront" data-target="http">The storefront uses http-client</li>
            <li data-edge data-source="storefront" data-target="auth">The storefront uses auth-sdk</li>
            <li data-edge data-source="storefront" data-target="images">The storefront uses image-resize</li>
            <li data-edge data-source="storefront" data-target="metrics">The storefront uses metrics</li>
            <li data-edge data-source="checkout" data-target="http">Checkout uses http-client</li>
            <li data-edge data-source="checkout" data-target="auth">Checkout uses auth-sdk</li>
            <li data-edge data-source="checkout" data-target="money">Checkout uses money</li>
            <li data-edge data-source="checkout" data-target="retry">Checkout uses retry-policy</li>
            <li data-edge data-source="checkout" data-target="metrics">Checkout uses metrics</li>
            <li data-edge data-source="payments" data-target="http">Payments uses http-client</li>
            <li data-edge data-source="payments" data-target="money">Payments uses money</li>
            <li data-edge data-source="payments" data-target="retry">Payments uses retry-policy</li>
            <li data-edge data-source="payments" data-target="metrics">Payments uses metrics</li>
            <li data-edge data-source="orders" data-target="money">Orders uses money</li>
            <li data-edge data-source="orders" data-target="retry">Orders uses retry-policy</li>
            <li data-edge data-source="orders" data-target="metrics">Orders uses metrics</li>
            <li data-edge data-source="invoices" data-target="money">Invoices uses money</li>
            <li data-edge data-source="invoices" data-target="pdf">Invoices uses pdf-render</li>
            <li data-edge data-source="invoices" data-target="templates">Invoices uses mail-templates</li>
            <li data-edge data-source="returns" data-target="money">Returns uses money</li>
            <li data-edge data-source="returns" data-target="pdf">Returns uses pdf-render</li>
            <li data-edge data-source="returns" data-target="auth">Returns uses auth-sdk</li>
            <li data-edge data-source="mail" data-target="templates">Order mails uses mail-templates</li>
            <li data-edge data-source="mail" data-target="retry">Order mails uses retry-policy</li>
            <li data-edge data-source="search" data-target="analyzers">Search uses text-analyzers</li>
            <li data-edge data-source="search" data-target="metrics">Search uses metrics</li>
            <li data-edge data-source="indexer" data-target="analyzers">The indexer uses text-analyzers</li>
            <li data-edge data-source="indexer" data-target="http">The indexer uses http-client</li>
            <li data-edge data-source="reports" data-target="pdf">Reports uses pdf-render</li>
            <li data-edge data-source="reports" data-target="money">Reports uses money</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch series-2"></span><span>Service</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--square series-5"></span><span>Shared library</span></span>
    </div>
</div>

Islands

A large graph read by its groups: one island per data-group, each shaped by its own springs, packed in rows as wide as the frame, with the islands of one data-cluster side by side. Springs alone melt groups into one ball, because the few links between groups pull harder than everything inside them — so a graph of teams, racks or projects wants islands.

The record whose id is a group's name is that group's heart, placed in the middle of its island: each team here. A record in no group that joins several islands — the event bus, the design system — is put between them, and a link across islands stays quiet until one of its ends is pointed at. With no data-group anywhere, islands falls back to force. The minimap sits at the top with .graph-minimap--top, where this drawing leaves room.

  • Web team
  • Storefront
  • Product pages
  • Account pages
  • Content editor
  • Checkout team
  • Cart
  • Checkout
  • Payments
  • Vouchers
  • Fraud check
  • Search team
  • Search API
  • Indexer
  • Suggestions
  • Ranking
  • Fulfilment team
  • Orders
  • Stock
  • Picking
  • Shipping
  • Returns
  • Parcel labels
  • Support tools team
  • Helpdesk
  • Refunds console
  • Customer overview
  • Infrastructure team
  • Gateway
  • Identity
  • Secrets
  • Build pipeline
  • Data team
  • Data warehouse
  • Nightly loader
  • Reports
  • Stock forecasts
  • Event bus
  • Design system
  • The web team owns the storefront
  • The web team owns the product pages
  • The web team owns the account pages
  • The web team owns the content editor
  • The storefront shows the product pages
  • The content editor publishes the product pages
  • The checkout team owns the cart
  • The checkout team owns checkout
  • The checkout team owns payments
  • The checkout team owns vouchers
  • The checkout team owns the fraud check
  • The cart hands over to checkout
  • Checkout charges through payments
  • Payments screens with the fraud check
  • The search team owns the search API
  • The search team owns the indexer
  • The search team owns suggestions
  • The search team owns ranking
  • The search API ranks with ranking
  • The fulfilment team owns orders
  • The fulfilment team owns stock
  • The fulfilment team owns picking
  • The fulfilment team owns shipping
  • The fulfilment team owns returns
  • The fulfilment team owns parcel labels
  • Orders reserves stock
  • Stock sends pick lists
  • Shipping prints parcel labels
  • The support tools team owns the helpdesk
  • The support tools team owns the refunds console
  • The support tools team owns the customer overview
  • The helpdesk opens the customer overview
  • The infrastructure team owns the gateway
  • The infrastructure team owns identity
  • The infrastructure team owns secrets
  • The infrastructure team owns the build pipeline
  • The gateway checks identity
  • The data team owns the data warehouse
  • The data team owns the nightly loader
  • The data team owns reports
  • The data team owns stock forecasts
  • The nightly loader fills the data warehouse
  • Reports read the data warehouse
  • The storefront calls the search API
  • The product pages show suggestions
  • The storefront fills the cart
  • Checkout places orders
  • The refunds console refunds through payments
  • The customer overview shows orders
  • The gateway routes to the storefront
  • The account pages sign in with identity
  • Checkout verifies with identity
  • The nightly loader reads orders
  • Stock forecasts feed stock
  • Orders publishes on the event bus
  • Shipping publishes on the event bus
  • Payments publishes on the event bus
  • The indexer listens on the event bus
  • The nightly loader listens on the event bus
  • The storefront is built with the design system
  • The account pages are built with the design system
  • The helpdesk is built with the design system
  • The refunds console is built with the design system
Commerce Operations Platform A team, at the heart of its island Shared by several teams
<div class="graph-frame">
    <div class="card">
        <div class="graph graph--lg" data-graph id="layout-islands" data-graph-layout="islands" data-graph-edge-labels="none"
             aria-label="Every team, the services it owns, and what they share">
            <ul class="graph-data" data-graph-data>
                <li data-node="web" data-group="web" data-cluster="commerce" data-tone="1" data-shape="hexagon" data-icon="ri-team-line">Web team</li>
                <li data-node="storefront" data-group="web" data-tone="1">Storefront</li>
                <li data-node="product-pages" data-group="web" data-tone="1">Product pages</li>
                <li data-node="account-pages" data-group="web" data-tone="1">Account pages</li>
                <li data-node="cms" data-group="web" data-tone="1">Content editor</li>

                <li data-node="checkout" data-group="checkout" data-cluster="commerce" data-tone="1" data-shape="hexagon" data-icon="ri-team-line">Checkout team</li>
                <li data-node="cart" data-group="checkout" data-tone="1">Cart</li>
                <li data-node="checkout-svc" data-group="checkout" data-tone="1">Checkout</li>
                <li data-node="payments" data-group="checkout" data-tone="1">Payments</li>
                <li data-node="vouchers" data-group="checkout" data-tone="1">Vouchers</li>
                <li data-node="fraud" data-group="checkout" data-tone="1">Fraud check</li>

                <li data-node="search" data-group="search" data-cluster="commerce" data-tone="1" data-shape="hexagon" data-icon="ri-team-line">Search team</li>
                <li data-node="search-api" data-group="search" data-tone="1">Search API</li>
                <li data-node="indexer" data-group="search" data-tone="1">Indexer</li>
                <li data-node="suggestions" data-group="search" data-tone="1">Suggestions</li>
                <li data-node="ranking" data-group="search" data-tone="1">Ranking</li>

                <li data-node="fulfilment" data-group="fulfilment" data-cluster="operations" data-tone="2" data-shape="hexagon" data-icon="ri-team-line">Fulfilment team</li>
                <li data-node="orders" data-group="fulfilment" data-tone="2">Orders</li>
                <li data-node="stock" data-group="fulfilment" data-tone="2">Stock</li>
                <li data-node="picking" data-group="fulfilment" data-tone="2">Picking</li>
                <li data-node="shipping" data-group="fulfilment" data-tone="2">Shipping</li>
                <li data-node="returns" data-group="fulfilment" data-tone="2">Returns</li>
                <li data-node="labels" data-group="fulfilment" data-tone="2">Parcel labels</li>

                <li data-node="support" data-group="support" data-cluster="operations" data-tone="2" data-shape="hexagon" data-icon="ri-team-line">Support tools team</li>
                <li data-node="helpdesk" data-group="support" data-tone="2">Helpdesk</li>
                <li data-node="refunds" data-group="support" data-tone="2">Refunds console</li>
                <li data-node="customer-360" data-group="support" data-tone="2">Customer overview</li>

                <li data-node="infra" data-group="infra" data-cluster="platform" data-tone="3" data-shape="hexagon" data-icon="ri-team-line">Infrastructure team</li>
                <li data-node="gateway" data-group="infra" data-tone="3">Gateway</li>
                <li data-node="identity" data-group="infra" data-tone="3">Identity</li>
                <li data-node="secrets" data-group="infra" data-tone="3">Secrets</li>
                <li data-node="ci" data-group="infra" data-tone="3">Build pipeline</li>

                <li data-node="data" data-group="data" data-cluster="platform" data-tone="3" data-shape="hexagon" data-icon="ri-team-line">Data team</li>
                <li data-node="warehouse" data-group="data" data-tone="3">Data warehouse</li>
                <li data-node="loader" data-group="data" data-tone="3">Nightly loader</li>
                <li data-node="reports" data-group="data" data-tone="3">Reports</li>
                <li data-node="forecasts" data-group="data" data-tone="3">Stock forecasts</li>

                <li data-node="event-bus" data-tone="muted" data-shape="diamond">Event bus</li>
                <li data-node="design-system" data-tone="muted" data-shape="diamond">Design system</li>

                <li data-edge data-source="web" data-target="storefront">The web team owns the storefront</li>
                <li data-edge data-source="web" data-target="product-pages">The web team owns the product pages</li>
                <li data-edge data-source="web" data-target="account-pages">The web team owns the account pages</li>
                <li data-edge data-source="web" data-target="cms">The web team owns the content editor</li>
                <li data-edge data-source="storefront" data-target="product-pages">The storefront shows the product pages</li>
                <li data-edge data-source="cms" data-target="product-pages">The content editor publishes the product pages</li>
                <li data-edge data-source="checkout" data-target="cart">The checkout team owns the cart</li>
                <li data-edge data-source="checkout" data-target="checkout-svc">The checkout team owns checkout</li>
                <li data-edge data-source="checkout" data-target="payments">The checkout team owns payments</li>
                <li data-edge data-source="checkout" data-target="vouchers">The checkout team owns vouchers</li>
                <li data-edge data-source="checkout" data-target="fraud">The checkout team owns the fraud check</li>
                <li data-edge data-source="cart" data-target="checkout-svc">The cart hands over to checkout</li>
                <li data-edge data-source="checkout-svc" data-target="payments">Checkout charges through payments</li>
                <li data-edge data-source="payments" data-target="fraud">Payments screens with the fraud check</li>
                <li data-edge data-source="search" data-target="search-api">The search team owns the search API</li>
                <li data-edge data-source="search" data-target="indexer">The search team owns the indexer</li>
                <li data-edge data-source="search" data-target="suggestions">The search team owns suggestions</li>
                <li data-edge data-source="search" data-target="ranking">The search team owns ranking</li>
                <li data-edge data-source="search-api" data-target="ranking">The search API ranks with ranking</li>
                <li data-edge data-source="fulfilment" data-target="orders">The fulfilment team owns orders</li>
                <li data-edge data-source="fulfilment" data-target="stock">The fulfilment team owns stock</li>
                <li data-edge data-source="fulfilment" data-target="picking">The fulfilment team owns picking</li>
                <li data-edge data-source="fulfilment" data-target="shipping">The fulfilment team owns shipping</li>
                <li data-edge data-source="fulfilment" data-target="returns">The fulfilment team owns returns</li>
                <li data-edge data-source="fulfilment" data-target="labels">The fulfilment team owns parcel labels</li>
                <li data-edge data-source="orders" data-target="stock">Orders reserves stock</li>
                <li data-edge data-source="stock" data-target="picking">Stock sends pick lists</li>
                <li data-edge data-source="shipping" data-target="labels">Shipping prints parcel labels</li>
                <li data-edge data-source="support" data-target="helpdesk">The support tools team owns the helpdesk</li>
                <li data-edge data-source="support" data-target="refunds">The support tools team owns the refunds console</li>
                <li data-edge data-source="support" data-target="customer-360">The support tools team owns the customer overview</li>
                <li data-edge data-source="helpdesk" data-target="customer-360">The helpdesk opens the customer overview</li>
                <li data-edge data-source="infra" data-target="gateway">The infrastructure team owns the gateway</li>
                <li data-edge data-source="infra" data-target="identity">The infrastructure team owns identity</li>
                <li data-edge data-source="infra" data-target="secrets">The infrastructure team owns secrets</li>
                <li data-edge data-source="infra" data-target="ci">The infrastructure team owns the build pipeline</li>
                <li data-edge data-source="gateway" data-target="identity">The gateway checks identity</li>
                <li data-edge data-source="data" data-target="warehouse">The data team owns the data warehouse</li>
                <li data-edge data-source="data" data-target="loader">The data team owns the nightly loader</li>
                <li data-edge data-source="data" data-target="reports">The data team owns reports</li>
                <li data-edge data-source="data" data-target="forecasts">The data team owns stock forecasts</li>
                <li data-edge data-source="loader" data-target="warehouse">The nightly loader fills the data warehouse</li>
                <li data-edge data-source="reports" data-target="warehouse">Reports read the data warehouse</li>

                <li data-edge data-source="storefront" data-target="search-api">The storefront calls the search API</li>
                <li data-edge data-source="product-pages" data-target="suggestions">The product pages show suggestions</li>
                <li data-edge data-source="storefront" data-target="cart">The storefront fills the cart</li>
                <li data-edge data-source="checkout-svc" data-target="orders">Checkout places orders</li>
                <li data-edge data-source="refunds" data-target="payments">The refunds console refunds through payments</li>
                <li data-edge data-source="customer-360" data-target="orders">The customer overview shows orders</li>
                <li data-edge data-source="gateway" data-target="storefront">The gateway routes to the storefront</li>
                <li data-edge data-source="account-pages" data-target="identity">The account pages sign in with identity</li>
                <li data-edge data-source="checkout-svc" data-target="identity">Checkout verifies with identity</li>
                <li data-edge data-source="loader" data-target="orders">The nightly loader reads orders</li>
                <li data-edge data-source="forecasts" data-target="stock">Stock forecasts feed stock</li>
                <li data-edge data-source="orders" data-target="event-bus">Orders publishes on the event bus</li>
                <li data-edge data-source="shipping" data-target="event-bus">Shipping publishes on the event bus</li>
                <li data-edge data-source="payments" data-target="event-bus">Payments publishes on the event bus</li>
                <li data-edge data-source="indexer" data-target="event-bus">The indexer listens on the event bus</li>
                <li data-edge data-source="loader" data-target="event-bus">The nightly loader listens on the event bus</li>
                <li data-edge data-source="storefront" data-target="design-system">The storefront is built with the design system</li>
                <li data-edge data-source="account-pages" data-target="design-system">The account pages are built with the design system</li>
                <li data-edge data-source="helpdesk" data-target="design-system">The helpdesk is built with the design system</li>
                <li data-edge data-source="refunds" data-target="design-system">The refunds console is built with the design system</li>
            </ul>
            <div class="graph-canvas"></div>
            <canvas class="graph-minimap graph-minimap--top" data-graph-minimap aria-hidden="true"></canvas>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch series-1"></span><span>Commerce</span></span>
        <span class="graph-key"><span class="graph-swatch series-2"></span><span>Operations</span></span>
        <span class="graph-key"><span class="graph-swatch series-3"></span><span>Platform</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--hexagon"></span><span>A team, at the heart of its island</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--diamond series-muted"></span><span>Shared by several teams</span></span>
    </div>
</div>

Rings around a record

Hops out from one record — the data-root, or the record a reader focused — then springs to settle them. The question is how far each record is from the centre, and the rings are the answer: here, what stops working while one database is down, and how directly. Each link is written the way the data has it, “Orders depends on src-db-14”, and data-graph-arrows="source" points it back, the way the failure travels.

  • src-db-14
  • Orders
  • Invoices
  • Returns
  • src-db-15
  • Backup
  • Checkout
  • Order tracking
  • Customer overview
  • Refunds console
  • Payment reminders
  • Warehouse loader
  • Storefront
  • Helpdesk
  • Reports
  • Shop app
  • Orders depends on src-db-14
  • Invoices depends on src-db-14
  • Returns depends on src-db-14
  • src-db-15 replicates from src-db-14
  • Backup copies src-db-14
  • Checkout depends on orders
  • Order tracking depends on orders
  • The customer overview depends on orders
  • The customer overview depends on invoices
  • The refunds console depends on returns
  • Payment reminders depend on invoices
  • The warehouse loader reads src-db-15
  • The storefront depends on checkout
  • The shop app depends on checkout
  • The shop app depends on order tracking
  • The helpdesk depends on the customer overview
  • Reports depend on the warehouse loader
Down Fails with it: one hop Degraded: two hops Barely noticed: three hops
<div class="graph-frame">
    <div class="graph" data-graph id="layout-rings" data-graph-layout="rings" data-graph-arrows="source"
         aria-label="What stops working while src-db-14 is down">
        <ul class="graph-data" data-graph-data>
            <li data-node="db" data-root data-tone="danger" data-icon="ri-database-2-line" data-meta="Down since 09:42">src-db-14</li>
            <li data-node="orders" data-tone="warn" data-icon="ri-file-list-3-line" data-meta="Fails with it">Orders</li>
            <li data-node="invoices" data-tone="warn" data-icon="ri-bill-line" data-meta="Fails with it">Invoices</li>
            <li data-node="returns" data-tone="warn" data-icon="ri-arrow-go-back-line" data-meta="Fails with it">Returns</li>
            <li data-node="replica" data-tone="warn" data-icon="ri-database-2-line" data-meta="Stops catching up">src-db-15</li>
            <li data-node="backup" data-tone="warn" data-icon="ri-save-3-line" data-meta="Skips tonight's copy">Backup</li>
            <li data-node="checkout" data-tone="2" data-icon="ri-secure-payment-line" data-meta="Cannot place orders">Checkout</li>
            <li data-node="tracking" data-tone="2" data-icon="ri-map-pin-line" data-meta="Shows stale orders">Order tracking</li>
            <li data-node="customer-360" data-tone="2" data-icon="ri-user-search-line" data-meta="Shows stale orders">Customer overview</li>
            <li data-node="refunds" data-tone="2" data-icon="ri-refund-2-line" data-meta="Cannot refund">Refunds console</li>
            <li data-node="dunning" data-tone="2" data-icon="ri-mail-send-line" data-meta="Sends no reminders">Payment reminders</li>
            <li data-node="loader" data-tone="2" data-icon="ri-timer-line" data-meta="Loads yesterday's data">Warehouse loader</li>
            <li data-node="storefront" data-tone="muted" data-icon="ri-store-2-line" data-meta="Browsing still works">Storefront</li>
            <li data-node="helpdesk" data-tone="muted" data-icon="ri-customer-service-2-line" data-meta="Answers slower">Helpdesk</li>
            <li data-node="reports" data-tone="muted" data-icon="ri-bar-chart-box-line" data-meta="One day behind">Reports</li>
            <li data-node="app" data-tone="muted" data-icon="ri-smartphone-line" data-meta="Browsing still works">Shop app</li>
            <li data-edge data-source="orders" data-target="db" data-label="depends on">Orders depends on src-db-14</li>
            <li data-edge data-source="invoices" data-target="db" data-label="depends on">Invoices depends on src-db-14</li>
            <li data-edge data-source="returns" data-target="db" data-label="depends on">Returns depends on src-db-14</li>
            <li data-edge data-source="replica" data-target="db" data-label="replicates">src-db-15 replicates from src-db-14</li>
            <li data-edge data-source="backup" data-target="db" data-label="copies">Backup copies src-db-14</li>
            <li data-edge data-source="checkout" data-target="orders" data-label="depends on">Checkout depends on orders</li>
            <li data-edge data-source="tracking" data-target="orders" data-label="depends on">Order tracking depends on orders</li>
            <li data-edge data-source="customer-360" data-target="orders" data-label="depends on">The customer overview depends on orders</li>
            <li data-edge data-source="customer-360" data-target="invoices" data-label="depends on">The customer overview depends on invoices</li>
            <li data-edge data-source="refunds" data-target="returns" data-label="depends on">The refunds console depends on returns</li>
            <li data-edge data-source="dunning" data-target="invoices" data-label="depends on">Payment reminders depend on invoices</li>
            <li data-edge data-source="loader" data-target="replica" data-label="reads">The warehouse loader reads src-db-15</li>
            <li data-edge data-source="storefront" data-target="checkout" data-label="depends on">The storefront depends on checkout</li>
            <li data-edge data-source="app" data-target="checkout" data-label="depends on">The shop app depends on checkout</li>
            <li data-edge data-source="app" data-target="tracking" data-label="depends on">The shop app depends on order tracking</li>
            <li data-edge data-source="helpdesk" data-target="customer-360" data-label="depends on">The helpdesk depends on the customer overview</li>
            <li data-edge data-source="reports" data-target="loader" data-label="depends on">Reports depend on the warehouse loader</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch series-danger"></span><span>Down</span></span>
        <span class="graph-key"><span class="graph-swatch series-warn"></span><span>Fails with it: one hop</span></span>
        <span class="graph-key"><span class="graph-swatch series-2"></span><span>Degraded: two hops</span></span>
        <span class="graph-key"><span class="graph-swatch series-muted"></span><span>Barely noticed: three hops</span></span>
    </div>
</div>

Concentric

The most connected in the middle, the least connected on the outside: what everything goes through, at a glance. The broker is the data-root, which takes the very middle whatever its count; its topics ring it, and the services that publish and listen are outermost.

  • Message broker
  • order-events
  • payment-events
  • stock-events
  • shipping-events
  • customer-events
  • Checkout
  • Orders
  • Payments
  • Stock
  • Warehouse
  • Shipping
  • Identity
  • Order mails
  • Indexer
  • Warehouse loader
  • Ledger
  • Order tracking
  • Customer overview
  • The broker carries order-events
  • The broker carries payment-events
  • The broker carries stock-events
  • The broker carries shipping-events
  • The broker carries customer-events
  • Checkout publishes order-events
  • Orders publishes order-events
  • Payments publishes payment-events
  • Stock publishes stock-events
  • The warehouse publishes stock-events
  • Shipping publishes shipping-events
  • Identity publishes customer-events
  • Order mails listen to order-events
  • Order mails listen to shipping-events
  • The indexer listens to order-events
  • The indexer listens to stock-events
  • The warehouse loader listens to order-events
  • The warehouse loader listens to payment-events
  • The ledger listens to payment-events
  • Order tracking listens to shipping-events
  • Order tracking listens to order-events
  • The customer overview listens to customer-events
  • The customer overview listens to order-events
<div class="graph" data-graph id="layout-concentric" data-graph-layout="concentric"
     aria-label="The message broker, its topics, and who publishes and listens">
    <ul class="graph-data" data-graph-data>
        <li data-node="broker" data-root data-tone="1" data-icon="ri-broadcast-line" data-meta="mq-01">Message broker</li>
        <li data-node="t-orders" data-tone="4" data-shape="diamond" data-meta="Topic">order-events</li>
        <li data-node="t-payments" data-tone="4" data-shape="diamond" data-meta="Topic">payment-events</li>
        <li data-node="t-stock" data-tone="4" data-shape="diamond" data-meta="Topic">stock-events</li>
        <li data-node="t-shipping" data-tone="4" data-shape="diamond" data-meta="Topic">shipping-events</li>
        <li data-node="t-customers" data-tone="4" data-shape="diamond" data-meta="Topic">customer-events</li>
        <li data-node="checkout" data-tone="2">Checkout</li>
        <li data-node="orders" data-tone="2">Orders</li>
        <li data-node="payments" data-tone="2">Payments</li>
        <li data-node="stock" data-tone="2">Stock</li>
        <li data-node="warehouse" data-tone="2">Warehouse</li>
        <li data-node="shipping" data-tone="2">Shipping</li>
        <li data-node="identity" data-tone="2">Identity</li>
        <li data-node="mail" data-tone="6">Order mails</li>
        <li data-node="indexer" data-tone="6">Indexer</li>
        <li data-node="loader" data-tone="6">Warehouse loader</li>
        <li data-node="ledger" data-tone="6">Ledger</li>
        <li data-node="tracking" data-tone="6">Order tracking</li>
        <li data-node="crm" data-tone="6">Customer overview</li>
        <li data-edge data-source="broker" data-target="t-orders" data-label="carries">The broker carries order-events</li>
        <li data-edge data-source="broker" data-target="t-payments" data-label="carries">The broker carries payment-events</li>
        <li data-edge data-source="broker" data-target="t-stock" data-label="carries">The broker carries stock-events</li>
        <li data-edge data-source="broker" data-target="t-shipping" data-label="carries">The broker carries shipping-events</li>
        <li data-edge data-source="broker" data-target="t-customers" data-label="carries">The broker carries customer-events</li>
        <li data-edge data-source="checkout" data-target="t-orders" data-label="publishes">Checkout publishes order-events</li>
        <li data-edge data-source="orders" data-target="t-orders" data-label="publishes">Orders publishes order-events</li>
        <li data-edge data-source="payments" data-target="t-payments" data-label="publishes">Payments publishes payment-events</li>
        <li data-edge data-source="stock" data-target="t-stock" data-label="publishes">Stock publishes stock-events</li>
        <li data-edge data-source="warehouse" data-target="t-stock" data-label="publishes">The warehouse publishes stock-events</li>
        <li data-edge data-source="shipping" data-target="t-shipping" data-label="publishes">Shipping publishes shipping-events</li>
        <li data-edge data-source="identity" data-target="t-customers" data-label="publishes">Identity publishes customer-events</li>
        <li data-edge data-source="t-orders" data-target="mail" data-label="listens">Order mails listen to order-events</li>
        <li data-edge data-source="t-shipping" data-target="mail" data-label="listens">Order mails listen to shipping-events</li>
        <li data-edge data-source="t-orders" data-target="indexer" data-label="listens">The indexer listens to order-events</li>
        <li data-edge data-source="t-stock" data-target="indexer" data-label="listens">The indexer listens to stock-events</li>
        <li data-edge data-source="t-orders" data-target="loader" data-label="listens">The warehouse loader listens to order-events</li>
        <li data-edge data-source="t-payments" data-target="loader" data-label="listens">The warehouse loader listens to payment-events</li>
        <li data-edge data-source="t-payments" data-target="ledger" data-label="listens">The ledger listens to payment-events</li>
        <li data-edge data-source="t-shipping" data-target="tracking" data-label="listens">Order tracking listens to shipping-events</li>
        <li data-edge data-source="t-orders" data-target="tracking" data-label="listens">Order tracking listens to order-events</li>
        <li data-edge data-source="t-customers" data-target="crm" data-label="listens">The customer overview listens to customer-events</li>
        <li data-edge data-source="t-orders" data-target="crm" data-label="listens">The customer overview listens to order-events</li>
    </ul>
    <div class="graph-canvas"></div>
    <div class="graph-wait skeleton skeleton-block"></div>
</div>

Tree

Breadth-first from the data-root, one rank per step, for data that really is a tree: a catalogue, a folder, a site map, where each record has one parent. data-graph-direction turns it — TB, the default, LR, BT or RL. For a hierarchy where a record has several parents, or boxes with their links labelled, use dagre below, which makes room between the ranks for both.

  • Northwind Retail
  • Beverages
  • Condiments
  • Confections
  • Dairy products
  • Seafood
  • Chai
  • Chang
  • Aniseed Syrup
  • Maple Syrup
  • Cranberry Sauce
  • Chocolate Biscuits
  • Pavlova
  • Scones
  • Queso Cabrales
  • Raclette
  • Clam Chowder
  • Crab Meat
  • Konbu
  • The catalogue lists beverages
  • The catalogue lists condiments
  • The catalogue lists confections
  • The catalogue lists dairy products
  • The catalogue lists seafood
  • Chai is a beverage
  • Chang is a beverage
  • Aniseed Syrup is a condiment
  • Maple Syrup is a condiment
  • Cranberry Sauce is a condiment
  • Chocolate Biscuits are a confection
  • Pavlova is a confection
  • Scones are a confection
  • Queso Cabrales is a dairy product
  • Raclette is a dairy product
  • Clam Chowder is seafood
  • Crab Meat is seafood
  • Konbu is seafood
<div class="graph" data-graph id="layout-tree" data-graph-layout="tree" data-graph-spacing="loose" data-graph-edge-labels="none"
     aria-label="The Northwind Retail catalogue, by category">
    <ul class="graph-data" data-graph-data>
        <li data-node="catalogue" data-root data-tone="1" data-icon="ri-book-shelf-line">Northwind Retail</li>
        <li data-node="beverages" data-tone="2" data-shape="rounded" data-icon="ri-cup-line">Beverages</li>
        <li data-node="condiments" data-tone="3" data-shape="rounded" data-icon="ri-restaurant-line">Condiments</li>
        <li data-node="confections" data-tone="4" data-shape="rounded" data-icon="ri-cake-3-line">Confections</li>
        <li data-node="dairy" data-tone="5" data-shape="rounded" data-icon="ri-drop-line">Dairy products</li>
        <li data-node="seafood" data-tone="6" data-shape="rounded" data-icon="ri-sailboat-line">Seafood</li>
        <li data-node="chai" data-tone="2">Chai</li>
        <li data-node="chang" data-tone="2">Chang</li>
        <li data-node="aniseed" data-tone="3">Aniseed Syrup</li>
        <li data-node="maple" data-tone="3">Maple Syrup</li>
        <li data-node="cranberry" data-tone="3">Cranberry Sauce</li>
        <li data-node="biscuits" data-tone="4">Chocolate Biscuits</li>
        <li data-node="pavlova" data-tone="4">Pavlova</li>
        <li data-node="scones" data-tone="4">Scones</li>
        <li data-node="cabrales" data-tone="5">Queso Cabrales</li>
        <li data-node="raclette" data-tone="5">Raclette</li>
        <li data-node="chowder" data-tone="6">Clam Chowder</li>
        <li data-node="crab" data-tone="6">Crab Meat</li>
        <li data-node="konbu" data-tone="6">Konbu</li>
        <li data-edge data-source="catalogue" data-target="beverages">The catalogue lists beverages</li>
        <li data-edge data-source="catalogue" data-target="condiments">The catalogue lists condiments</li>
        <li data-edge data-source="catalogue" data-target="confections">The catalogue lists confections</li>
        <li data-edge data-source="catalogue" data-target="dairy">The catalogue lists dairy products</li>
        <li data-edge data-source="catalogue" data-target="seafood">The catalogue lists seafood</li>
        <li data-edge data-source="beverages" data-target="chai">Chai is a beverage</li>
        <li data-edge data-source="beverages" data-target="chang">Chang is a beverage</li>
        <li data-edge data-source="condiments" data-target="aniseed">Aniseed Syrup is a condiment</li>
        <li data-edge data-source="condiments" data-target="maple">Maple Syrup is a condiment</li>
        <li data-edge data-source="condiments" data-target="cranberry">Cranberry Sauce is a condiment</li>
        <li data-edge data-source="confections" data-target="biscuits">Chocolate Biscuits are a confection</li>
        <li data-edge data-source="confections" data-target="pavlova">Pavlova is a confection</li>
        <li data-edge data-source="confections" data-target="scones">Scones are a confection</li>
        <li data-edge data-source="dairy" data-target="cabrales">Queso Cabrales is a dairy product</li>
        <li data-edge data-source="dairy" data-target="raclette">Raclette is a dairy product</li>
        <li data-edge data-source="seafood" data-target="chowder">Clam Chowder is seafood</li>
        <li data-edge data-source="seafood" data-target="crab">Crab Meat is seafood</li>
        <li data-edge data-source="seafood" data-target="konbu">Konbu is seafood</li>
    </ul>
    <div class="graph-canvas"></div>
    <div class="graph-wait skeleton skeleton-block"></div>
</div>

A hierarchy, top down

dagre lays records out in ranks with every link pointing one way: an org chart, a dependency tree, a flow. In boxes, with data-graph-curve="taxi", the links turn at right angles, which is how an org chart is drawn. Every line in an org chart says “reports to”, so this one writes none.

  • Alex Fischer
  • Jordan Weiss
  • Priya Nair
  • Rin Nakamura
  • Web team
  • Checkout team
  • Search team
  • Infrastructure
  • Developer tools
  • Analytics
  • Data engineering
  • Jordan Weiss reports to Alex Fischer
  • Priya Nair reports to Alex Fischer
  • Rin Nakamura reports to Alex Fischer
  • The web team reports to Jordan Weiss
  • The checkout team reports to Jordan Weiss
  • The search team reports to Jordan Weiss
  • Infrastructure reports to Priya Nair
  • Developer tools reports to Priya Nair
  • Analytics reports to Rin Nakamura
  • Data engineering reports to Rin Nakamura
<div class="card">
    <div class="graph" data-graph id="layout-org" data-graph-layout="dagre" data-graph-direction="TB"
         data-graph-nodes="box" data-graph-curve="taxi" data-graph-edge-labels="none" aria-label="Engineering, by department and team">
        <ul class="graph-data" data-graph-data>
            <li data-node="alex" data-root data-tone="1" data-icon="ri-user-star-line" data-meta="Head of Engineering">Alex Fischer</li>
            <li data-node="jordan" data-tone="2" data-icon="ri-user-3-line" data-meta="Commerce">Jordan Weiss</li>
            <li data-node="priya" data-tone="3" data-icon="ri-user-3-line" data-meta="Platform">Priya Nair</li>
            <li data-node="rin" data-tone="4" data-icon="ri-user-3-line" data-meta="Data">Rin Nakamura</li>
            <li data-node="web" data-tone="2" data-icon="ri-team-line" data-meta="6 engineers">Web team</li>
            <li data-node="checkout" data-tone="2" data-icon="ri-team-line" data-meta="5 engineers">Checkout team</li>
            <li data-node="search" data-tone="2" data-icon="ri-team-line" data-meta="4 engineers">Search team</li>
            <li data-node="infra" data-tone="3" data-icon="ri-team-line" data-meta="5 engineers">Infrastructure</li>
            <li data-node="tools" data-tone="3" data-icon="ri-team-line" data-meta="3 engineers">Developer tools</li>
            <li data-node="analytics" data-tone="4" data-icon="ri-team-line" data-meta="4 analysts">Analytics</li>
            <li data-node="pipelines" data-tone="4" data-icon="ri-team-line" data-meta="3 engineers">Data engineering</li>
            <li data-edge data-source="alex" data-target="jordan">Jordan Weiss reports to Alex Fischer</li>
            <li data-edge data-source="alex" data-target="priya">Priya Nair reports to Alex Fischer</li>
            <li data-edge data-source="alex" data-target="rin">Rin Nakamura reports to Alex Fischer</li>
            <li data-edge data-source="jordan" data-target="web">The web team reports to Jordan Weiss</li>
            <li data-edge data-source="jordan" data-target="checkout">The checkout team reports to Jordan Weiss</li>
            <li data-edge data-source="jordan" data-target="search">The search team reports to Jordan Weiss</li>
            <li data-edge data-source="priya" data-target="infra">Infrastructure reports to Priya Nair</li>
            <li data-edge data-source="priya" data-target="tools">Developer tools reports to Priya Nair</li>
            <li data-edge data-source="rin" data-target="analytics">Analytics reports to Rin Nakamura</li>
            <li data-edge data-source="rin" data-target="pipelines">Data engineering reports to Rin Nakamura</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
</div>

A flow, left to right

The same layout with data-graph-direction="LR", so a pipeline reads in the order it runs. The three checks share a rank because each depends on the build and on nothing between them. Each stage's tone is its result and the legend says which is which; the stage that has not started is data-muted. data-graph-curve="round-taxi" rounds the corners of the right angles.

  • Build
  • Lint
  • Unit tests
  • Security scan
  • Package
  • Staging
  • Production
  • The build is linted
  • The build is unit tested
  • The build is scanned for vulnerabilities
  • Lint must pass before packaging
  • Unit tests must pass before packaging
  • The security scan must pass before packaging
  • The package is deployed to staging and smoke tested
  • Staging is released to production once approved
Passed Passed on a retry Running Waiting
<div class="graph-frame">
    <div class="card">
        <div class="graph graph--sm" data-graph id="layout-pipeline" data-graph-layout="dagre" data-graph-direction="LR"
             data-graph-nodes="box" data-graph-arrows="target" data-graph-curve="round-taxi" data-graph-edge-labels="none"
             aria-label="Release 2.14.1, stage by stage">
            <ul class="graph-data" data-graph-data>
                <li data-node="build" data-tone="go" data-icon="ri-hammer-line" data-meta="a3f9c1e · 3 min 12 s">Build</li>
                <li data-node="lint" data-tone="go" data-icon="ri-checkbox-circle-line" data-meta="No warnings">Lint</li>
                <li data-node="unit" data-tone="warn" data-icon="ri-flask-line" data-meta="Passed on retry">Unit tests</li>
                <li data-node="scan" data-tone="go" data-icon="ri-shield-check-line" data-meta="No findings">Security scan</li>
                <li data-node="package" data-tone="go" data-icon="ri-archive-2-line" data-meta="v2.14.1 · 84 MB">Package</li>
                <li data-node="staging" data-tone="info" data-icon="ri-loader-4-line" data-meta="Smoke tests, 12 of 30">Staging</li>
                <li data-node="production" data-muted data-icon="ri-rocket-2-line" data-meta="Needs approval">Production</li>
                <li data-edge data-source="build" data-target="lint">The build is linted</li>
                <li data-edge data-source="build" data-target="unit">The build is unit tested</li>
                <li data-edge data-source="build" data-target="scan">The build is scanned for vulnerabilities</li>
                <li data-edge data-source="lint" data-target="package">Lint must pass before packaging</li>
                <li data-edge data-source="unit" data-target="package">Unit tests must pass before packaging</li>
                <li data-edge data-source="scan" data-target="package">The security scan must pass before packaging</li>
                <li data-edge data-source="package" data-target="staging">The package is deployed to staging and smoke tested</li>
                <li data-edge data-source="staging" data-target="production" data-line="dashed">Staging is released to production once approved</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch graph-swatch--rounded series-go"></span><span>Passed</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--rounded series-warn"></span><span>Passed on a retry</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--rounded series-info"></span><span>Running</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--rounded graph-swatch--muted series-muted"></span><span>Waiting</span></span>
    </div>
</div>

Groups inside groups

data-parent draws a record inside another — hosts in racks, racks in sites. The parent is drawn as an outline in its tone with its name at the top, and fcose, springs that understand nesting, lays each group out around its members and the groups around each other. A link can cross groups, as the replication between the sites does, and a record can stand outside every group. The key for a group is .graph-swatch--group. Folding a group away is on Graph plugins.

  • North site
  • South site
  • Rack A1
  • Rack A2
  • Rack B1
  • Rack B2
  • web-01
  • web-02
  • api-01
  • src-db-14
  • cache-01
  • web-03
  • api-02
  • build-runner-04
  • src-db-15
  • backup-01
  • lb-01
  • lb-01 sends traffic to web-01
  • lb-01 sends traffic to web-02
  • lb-01 sends traffic to web-03
  • web-01 calls api-01
  • web-02 calls api-01
  • web-03 calls api-02
  • api-01 writes to src-db-14
  • api-02 writes to src-db-14
  • api-01 caches in cache-01
  • api-02 caches in cache-01
  • src-db-14 replicates to src-db-15, across sites
  • backup-01 copies src-db-15 every night
  • build-runner-04 deploys to api-02
  • build-runner-04 deploys to api-01
North site South site Replication Deploys
<div class="graph-frame">
    <div class="graph" data-graph id="layout-compound" data-graph-layout="fcose"
         aria-label="Hosts, in the racks and the sites they stand in">
        <ul class="graph-data" data-graph-data>
            <li data-node="north" data-tone="1">North site</li>
            <li data-node="south" data-tone="2">South site</li>
            <li data-node="rack-a1" data-parent="north" data-tone="1">Rack A1</li>
            <li data-node="rack-a2" data-parent="north" data-tone="1">Rack A2</li>
            <li data-node="rack-b1" data-parent="south" data-tone="2">Rack B1</li>
            <li data-node="rack-b2" data-parent="south" data-tone="2">Rack B2</li>
            <li data-node="web-01" data-parent="rack-a1" data-tone="1" data-meta="198.51.100.51">web-01</li>
            <li data-node="web-02" data-parent="rack-a1" data-tone="1" data-meta="198.51.100.52">web-02</li>
            <li data-node="api-01" data-parent="rack-a1" data-tone="1" data-meta="198.51.100.61">api-01</li>
            <li data-node="src-db-14" data-parent="rack-a2" data-tone="1" data-shape="barrel" data-meta="198.51.100.21 · primary">src-db-14</li>
            <li data-node="cache-01" data-parent="rack-a2" data-tone="1" data-meta="198.51.100.71">cache-01</li>
            <li data-node="web-03" data-parent="rack-b1" data-tone="2" data-meta="198.51.100.53">web-03</li>
            <li data-node="api-02" data-parent="rack-b1" data-tone="2" data-meta="198.51.100.62">api-02</li>
            <li data-node="build-runner-04" data-parent="rack-b1" data-tone="2" data-meta="198.51.100.44">build-runner-04</li>
            <li data-node="src-db-15" data-parent="rack-b2" data-tone="2" data-shape="barrel" data-meta="198.51.100.22 · replica">src-db-15</li>
            <li data-node="backup-01" data-parent="rack-b2" data-tone="2" data-meta="198.51.100.81">backup-01</li>
            <li data-node="lb" data-tone="muted" data-shape="diamond" data-meta="203.0.113.10">lb-01</li>
            <li data-edge data-source="lb" data-target="web-01" data-label="traffic">lb-01 sends traffic to web-01</li>
            <li data-edge data-source="lb" data-target="web-02" data-label="traffic">lb-01 sends traffic to web-02</li>
            <li data-edge data-source="lb" data-target="web-03" data-label="traffic">lb-01 sends traffic to web-03</li>
            <li data-edge data-source="web-01" data-target="api-01" data-label="calls">web-01 calls api-01</li>
            <li data-edge data-source="web-02" data-target="api-01" data-label="calls">web-02 calls api-01</li>
            <li data-edge data-source="web-03" data-target="api-02" data-label="calls">web-03 calls api-02</li>
            <li data-edge data-source="api-01" data-target="src-db-14" data-label="writes">api-01 writes to src-db-14</li>
            <li data-edge data-source="api-02" data-target="src-db-14" data-label="writes">api-02 writes to src-db-14</li>
            <li data-edge data-source="api-01" data-target="cache-01" data-label="caches">api-01 caches in cache-01</li>
            <li data-edge data-source="api-02" data-target="cache-01" data-label="caches">api-02 caches in cache-01</li>
            <li data-edge data-source="src-db-14" data-target="src-db-15" data-line="dashed" data-label="replicates">src-db-14 replicates to src-db-15, across sites</li>
            <li data-edge data-source="backup-01" data-target="src-db-15" data-label="nightly copy">backup-01 copies src-db-15 every night</li>
            <li data-edge data-source="build-runner-04" data-target="api-02" data-line="dotted" data-label="deploys">build-runner-04 deploys to api-02</li>
            <li data-edge data-source="build-runner-04" data-target="api-01" data-line="dotted" data-label="deploys">build-runner-04 deploys to api-01</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
    <div class="graph-legend">
        <span class="graph-key"><span class="graph-swatch graph-swatch--group series-1"></span><span>North site</span></span>
        <span class="graph-key"><span class="graph-swatch graph-swatch--group series-2"></span><span>South site</span></span>
        <span class="graph-key"><span class="graph-line graph-line--dashed"></span><span>Replication</span></span>
        <span class="graph-key"><span class="graph-line graph-line--dotted"></span><span>Deploys</span></span>
    </div>
</div>

Grid and circle

Busiest first, in reading order. grid is for records whose links matter less than seeing every one — a fleet, a set of flags — and circle for a ring, where the order is the point. Both sort by how many links a record has and then by id, which is why the cache nodes go round in their own order and the one rebalancing keeps its place.

The fleet, busiest first

  • lb-01
  • web-01
  • web-02
  • web-03
  • api-01
  • api-02
  • cache-01
  • src-db-14
  • src-db-15
  • backup-01
  • log-01
  • build-runner-04
  • lb-01 sends traffic to web-01
  • lb-01 sends traffic to web-02
  • lb-01 sends traffic to web-03
  • web-01 calls api-01
  • web-02 calls api-01
  • web-03 calls api-02
  • api-01 writes to src-db-14
  • api-02 writes to src-db-14
  • api-01 caches in cache-01
  • api-02 caches in cache-01
  • src-db-14 replicates to src-db-15
  • backup-01 copies src-db-15
  • api-01 ships its logs to log-01
  • api-02 ships its logs to log-01
  • build-runner-04 deploys to api-01

The session cache ring

  • cache-01
  • cache-02
  • cache-03
  • cache-04
  • cache-05
  • cache-06
  • cache-07
  • cache-08
  • cache-01 hands over to cache-02
  • cache-02 hands over to cache-03
  • cache-03 hands over to cache-04
  • cache-04 hands over to cache-05
  • cache-05 hands over to cache-06
  • cache-06 hands over to cache-07
  • cache-07 hands over to cache-08
  • cache-08 hands over to cache-01
<div class="sedna-grid sedna-grid--lg">
    <div class="card">
        <div class="card-head"><h3>The fleet, busiest first</h3></div>
        <div class="card-body card-body--flush">
            <div class="graph graph--sm" data-graph id="layout-grid" data-graph-layout="grid" data-graph-spacing="loose" data-graph-edge-labels="none"
                 aria-label="Every host, the busiest first">
                <ul class="graph-data" data-graph-data>
                    <li data-node="lb-01" data-tone="1">lb-01</li>
                    <li data-node="web-01" data-tone="2">web-01</li>
                    <li data-node="web-02" data-tone="2">web-02</li>
                    <li data-node="web-03" data-tone="2">web-03</li>
                    <li data-node="api-01" data-tone="3">api-01</li>
                    <li data-node="api-02" data-tone="3">api-02</li>
                    <li data-node="cache-01" data-tone="4">cache-01</li>
                    <li data-node="src-db-14" data-tone="5">src-db-14</li>
                    <li data-node="src-db-15" data-tone="5">src-db-15</li>
                    <li data-node="backup-01" data-tone="6">backup-01</li>
                    <li data-node="log-01" data-tone="6">log-01</li>
                    <li data-node="build-runner-04" data-tone="6">build-runner-04</li>
                    <li data-edge data-source="lb-01" data-target="web-01">lb-01 sends traffic to web-01</li>
                    <li data-edge data-source="lb-01" data-target="web-02">lb-01 sends traffic to web-02</li>
                    <li data-edge data-source="lb-01" data-target="web-03">lb-01 sends traffic to web-03</li>
                    <li data-edge data-source="web-01" data-target="api-01">web-01 calls api-01</li>
                    <li data-edge data-source="web-02" data-target="api-01">web-02 calls api-01</li>
                    <li data-edge data-source="web-03" data-target="api-02">web-03 calls api-02</li>
                    <li data-edge data-source="api-01" data-target="src-db-14">api-01 writes to src-db-14</li>
                    <li data-edge data-source="api-02" data-target="src-db-14">api-02 writes to src-db-14</li>
                    <li data-edge data-source="api-01" data-target="cache-01">api-01 caches in cache-01</li>
                    <li data-edge data-source="api-02" data-target="cache-01">api-02 caches in cache-01</li>
                    <li data-edge data-source="src-db-14" data-target="src-db-15">src-db-14 replicates to src-db-15</li>
                    <li data-edge data-source="backup-01" data-target="src-db-15">backup-01 copies src-db-15</li>
                    <li data-edge data-source="api-01" data-target="log-01">api-01 ships its logs to log-01</li>
                    <li data-edge data-source="api-02" data-target="log-01">api-02 ships its logs to log-01</li>
                    <li data-edge data-source="build-runner-04" data-target="api-01">build-runner-04 deploys to api-01</li>
                </ul>
                <div class="graph-canvas"></div>
                <div class="graph-wait skeleton skeleton-block"></div>
            </div>
        </div>
    </div>
    <div class="card">
        <div class="card-head"><h3>The session cache ring</h3></div>
        <div class="card-body card-body--flush">
            <div class="graph graph--sm" data-graph id="layout-circle" data-graph-layout="circle" data-graph-edge-labels="none"
                 aria-label="The session cache nodes, each handing over to the next">
                <ul class="graph-data" data-graph-data>
                    <li data-node="cache-01" data-tone="4" data-meta="Keys 0 to 1FFF">cache-01</li>
                    <li data-node="cache-02" data-tone="4" data-meta="Keys 2000 to 3FFF">cache-02</li>
                    <li data-node="cache-03" data-tone="4" data-meta="Keys 4000 to 5FFF">cache-03</li>
                    <li data-node="cache-04" data-tone="4" data-meta="Keys 6000 to 7FFF">cache-04</li>
                    <li data-node="cache-05" data-tone="4" data-meta="Keys 8000 to 9FFF">cache-05</li>
                    <li data-node="cache-06" data-tone="warn" data-meta="Keys A000 to BFFF · rebalancing">cache-06</li>
                    <li data-node="cache-07" data-tone="4" data-meta="Keys C000 to DFFF">cache-07</li>
                    <li data-node="cache-08" data-tone="4" data-meta="Keys E000 to FFFF">cache-08</li>
                    <li data-edge data-source="cache-01" data-target="cache-02">cache-01 hands over to cache-02</li>
                    <li data-edge data-source="cache-02" data-target="cache-03">cache-02 hands over to cache-03</li>
                    <li data-edge data-source="cache-03" data-target="cache-04">cache-03 hands over to cache-04</li>
                    <li data-edge data-source="cache-04" data-target="cache-05">cache-04 hands over to cache-05</li>
                    <li data-edge data-source="cache-05" data-target="cache-06">cache-05 hands over to cache-06</li>
                    <li data-edge data-source="cache-06" data-target="cache-07">cache-06 hands over to cache-07</li>
                    <li data-edge data-source="cache-07" data-target="cache-08">cache-07 hands over to cache-08</li>
                    <li data-edge data-source="cache-08" data-target="cache-01">cache-08 hands over to cache-01</li>
                </ul>
                <div class="graph-canvas"></div>
                <div class="graph-wait skeleton skeleton-block"></div>
            </div>
        </div>
    </div>
</div>

Positions the app knows

preset puts each record at its data-x and data-y: a floor plan, a map, a diagram someone arranged and saved. The numbers are the drawing's own units, and the view fits them to the frame, so they need no relation to pixels. Give every record a position. data-graph-drag="false" keeps a reader from moving a dock door.

  • Dock 1
  • Dock 2
  • Receiving
  • Quality check
  • Put-away
  • Aisle A
  • Aisle B
  • Aisle C
  • Aisle D
  • Picking
  • Packing
  • Dispatch
  • Dock 3
  • Goods from dock 1 go to receiving
  • Goods from dock 2 go to receiving
  • Returns from dock 2 go to the quality check
  • Receiving sends a sample to the quality check
  • Receiving hands over to put-away
  • Checked goods go to put-away
  • Put-away stocks aisle A
  • Put-away stocks aisle B
  • Put-away stocks aisle C
  • Put-away stocks aisle D
  • Picking takes from aisle A
  • Picking takes from aisle B
  • Picking takes from aisle C
  • Picking takes from aisle D
  • Picking hands over to packing
  • Packing hands over to dispatch
  • Dispatch loads at dock 3
<div class="card">
    <div class="graph" data-graph id="layout-preset" data-graph-layout="preset" data-graph-nodes="box"
         data-graph-arrows="target" data-graph-edge-labels="none" data-graph-drag="false"
         aria-label="The warehouse floor, and the way goods move across it">
        <ul class="graph-data" data-graph-data>
            <li data-node="dock-1" data-x="0" data-y="0" data-tone="2" data-icon="ri-truck-line" data-meta="Inbound">Dock 1</li>
            <li data-node="dock-2" data-x="0" data-y="150" data-tone="2" data-icon="ri-truck-line" data-meta="Inbound and returns">Dock 2</li>
            <li data-node="receiving" data-x="180" data-y="75" data-tone="2" data-icon="ri-inbox-archive-line" data-meta="Count and label">Receiving</li>
            <li data-node="quality" data-x="180" data-y="230" data-tone="6" data-icon="ri-search-eye-line" data-meta="One pallet in twenty">Quality check</li>
            <li data-node="put-away" data-x="360" data-y="75" data-tone="2" data-icon="ri-stack-line" data-meta="To a free slot">Put-away</li>
            <li data-node="aisle-a" data-x="550" data-y="-60" data-tone="4" data-icon="ri-layout-column-line" data-meta="Small parts">Aisle A</li>
            <li data-node="aisle-b" data-x="550" data-y="30" data-tone="4" data-icon="ri-layout-column-line" data-meta="Fast movers">Aisle B</li>
            <li data-node="aisle-c" data-x="550" data-y="120" data-tone="info" data-icon="ri-temp-cold-line" data-meta="Chilled, 2 to 6 °C">Aisle C</li>
            <li data-node="aisle-d" data-x="550" data-y="210" data-tone="4" data-icon="ri-layout-column-line" data-meta="Bulky goods">Aisle D</li>
            <li data-node="picking" data-x="740" data-y="75" data-tone="3" data-icon="ri-hand-coin-line" data-meta="Waves every 20 min">Picking</li>
            <li data-node="packing" data-x="920" data-y="75" data-tone="3" data-icon="ri-gift-line" data-meta="6 stations">Packing</li>
            <li data-node="dispatch" data-x="1100" data-y="75" data-tone="3" data-icon="ri-send-plane-line" data-meta="Carrier pick-up 16:00">Dispatch</li>
            <li data-node="dock-3" data-x="1100" data-y="200" data-tone="3" data-icon="ri-truck-line" data-meta="Outbound">Dock 3</li>
            <li data-edge data-source="dock-1" data-target="receiving">Goods from dock 1 go to receiving</li>
            <li data-edge data-source="dock-2" data-target="receiving">Goods from dock 2 go to receiving</li>
            <li data-edge data-source="dock-2" data-target="quality" data-line="dashed">Returns from dock 2 go to the quality check</li>
            <li data-edge data-source="receiving" data-target="quality" data-line="dashed">Receiving sends a sample to the quality check</li>
            <li data-edge data-source="receiving" data-target="put-away">Receiving hands over to put-away</li>
            <li data-edge data-source="quality" data-target="put-away">Checked goods go to put-away</li>
            <li data-edge data-source="put-away" data-target="aisle-a">Put-away stocks aisle A</li>
            <li data-edge data-source="put-away" data-target="aisle-b">Put-away stocks aisle B</li>
            <li data-edge data-source="put-away" data-target="aisle-c">Put-away stocks aisle C</li>
            <li data-edge data-source="put-away" data-target="aisle-d">Put-away stocks aisle D</li>
            <li data-edge data-source="aisle-a" data-target="picking">Picking takes from aisle A</li>
            <li data-edge data-source="aisle-b" data-target="picking">Picking takes from aisle B</li>
            <li data-edge data-source="aisle-c" data-target="picking">Picking takes from aisle C</li>
            <li data-edge data-source="aisle-d" data-target="picking">Picking takes from aisle D</li>
            <li data-edge data-source="picking" data-target="packing">Picking hands over to packing</li>
            <li data-edge data-source="packing" data-target="dispatch">Packing hands over to dispatch</li>
            <li data-edge data-source="dispatch" data-target="dock-3">Dispatch loads at dock 3</li>
        </ul>
        <div class="graph-canvas"></div>
        <div class="graph-wait skeleton skeleton-block"></div>
    </div>
</div>

Switching layouts

Live — try each layout. A select with data-graph-option="layout" switches it, radios with data-graph-option="direction" turn a tree or a hierarchy, and data-graph-option="spacing" chooses compact, normal or loose — what data-graph-spacing sets on the graph itself. The records travel from where they were to where the new layout puts them, so a reader can follow one across the change. These records have a root for the rings, groups for the islands and links that run one way for a hierarchy. Data without them is still drawn in every layout — islands with no groups falls back to force — but the layout has less to say.

  • Storefront
  • Product pages
  • Account pages
  • Search API
  • Suggestions
  • Indexer
  • Cart
  • Checkout
  • Payments
  • Fraud check
  • Orders
  • Stock
  • Shipping
  • Parcel labels
  • Identity
  • Order mails
  • Audit log
  • The storefront shows the product pages
  • The storefront shows the account pages
  • The storefront calls the search API
  • The product pages show suggestions
  • The search API reads what the indexer built
  • The storefront fills the cart
  • The cart hands over to checkout
  • Checkout charges through payments
  • Payments screens with the fraud check
  • Checkout places the order
  • Orders reserves stock
  • Orders books shipping
  • Shipping prints parcel labels
  • The account pages sign in with identity
  • Checkout verifies with identity
  • Orders sends the confirmation
  • Shipping sends the tracking mail
  • Payments records every charge
  • Identity records every sign-in
<div class="graph-frame" data-graph-frame>
    <div class="toolbar">
        <div class="toolbar-filters">
            <select class="form-input form-select form-select--sm sedna-w-fit" aria-label="Layout" data-graph-option="layout">
                <option value="force" selected>Force</option>
                <option value="islands">Islands</option>
                <option value="rings">Rings</option>
                <option value="concentric">Concentric</option>
                <option value="tree">Tree</option>
                <option value="dagre">Hierarchy</option>
                <option value="fcose">Compound springs</option>
                <option value="grid">Grid</option>
                <option value="circle">Circle</option>
                <option value="preset">Preset</option>
            </select>
            <div class="segmented segmented--sm" role="group" aria-label="Direction, for a tree or a hierarchy">
                <label class="segmented-option" data-tip="Top to bottom">
                    <input type="radio" name="layout-switch-direction" value="TB" data-graph-option="direction" checked />
                    <span><i class="ri-arrow-down-line" aria-hidden="true"></i><span class="visually-hidden">Top to bottom</span></span>
                </label>
                <label class="segmented-option" data-tip="Left to right">
                    <input type="radio" name="layout-switch-direction" value="LR" data-graph-option="direction" />
                    <span><i class="ri-arrow-right-line" aria-hidden="true"></i><span class="visually-hidden">Left to right</span></span>
                </label>
                <label class="segmented-option" data-tip="Bottom to top">
                    <input type="radio" name="layout-switch-direction" value="BT" data-graph-option="direction" />
                    <span><i class="ri-arrow-up-line" aria-hidden="true"></i><span class="visually-hidden">Bottom to top</span></span>
                </label>
                <label class="segmented-option" data-tip="Right to left">
                    <input type="radio" name="layout-switch-direction" value="RL" data-graph-option="direction" />
                    <span><i class="ri-arrow-left-line" aria-hidden="true"></i><span class="visually-hidden">Right to left</span></span>
                </label>
            </div>
            <select class="form-input form-select form-select--sm sedna-w-fit" aria-label="Spacing" data-graph-option="spacing">
                <option value="compact">Compact</option>
                <option value="normal" selected>Normal</option>
                <option value="loose">Loose</option>
            </select>
        </div>
    </div>
    <div class="card">
        <div class="graph" data-graph id="layout-switch" data-graph-arrows="target" data-graph-edge-labels="none"
             aria-label="The shop's services, from the storefront down">
            <ul class="graph-data" data-graph-data>
                <li data-node="storefront" data-root data-group="web" data-tone="1" data-icon="ri-store-2-line">Storefront</li>
                <li data-node="product-pages" data-group="web" data-tone="1">Product pages</li>
                <li data-node="account-pages" data-group="web" data-tone="1">Account pages</li>
                <li data-node="search-api" data-group="search" data-tone="2">Search API</li>
                <li data-node="suggestions" data-group="search" data-tone="2">Suggestions</li>
                <li data-node="indexer" data-group="search" data-tone="2">Indexer</li>
                <li data-node="cart" data-group="checkout" data-tone="3">Cart</li>
                <li data-node="checkout" data-group="checkout" data-tone="3">Checkout</li>
                <li data-node="payments" data-group="checkout" data-tone="3">Payments</li>
                <li data-node="fraud" data-group="checkout" data-tone="3">Fraud check</li>
                <li data-node="orders" data-group="fulfilment" data-tone="4">Orders</li>
                <li data-node="stock" data-group="fulfilment" data-tone="4">Stock</li>
                <li data-node="shipping" data-group="fulfilment" data-tone="4">Shipping</li>
                <li data-node="labels" data-group="fulfilment" data-tone="4">Parcel labels</li>
                <li data-node="identity" data-group="platform" data-tone="5">Identity</li>
                <li data-node="mail" data-group="platform" data-tone="5">Order mails</li>
                <li data-node="audit" data-group="platform" data-tone="5">Audit log</li>
                <li data-edge data-source="storefront" data-target="product-pages">The storefront shows the product pages</li>
                <li data-edge data-source="storefront" data-target="account-pages">The storefront shows the account pages</li>
                <li data-edge data-source="storefront" data-target="search-api">The storefront calls the search API</li>
                <li data-edge data-source="product-pages" data-target="suggestions">The product pages show suggestions</li>
                <li data-edge data-source="search-api" data-target="indexer">The search API reads what the indexer built</li>
                <li data-edge data-source="storefront" data-target="cart">The storefront fills the cart</li>
                <li data-edge data-source="cart" data-target="checkout">The cart hands over to checkout</li>
                <li data-edge data-source="checkout" data-target="payments">Checkout charges through payments</li>
                <li data-edge data-source="payments" data-target="fraud">Payments screens with the fraud check</li>
                <li data-edge data-source="checkout" data-target="orders">Checkout places the order</li>
                <li data-edge data-source="orders" data-target="stock">Orders reserves stock</li>
                <li data-edge data-source="orders" data-target="shipping">Orders books shipping</li>
                <li data-edge data-source="shipping" data-target="labels">Shipping prints parcel labels</li>
                <li data-edge data-source="account-pages" data-target="identity">The account pages sign in with identity</li>
                <li data-edge data-source="checkout" data-target="identity">Checkout verifies with identity</li>
                <li data-edge data-source="orders" data-target="mail">Orders sends the confirmation</li>
                <li data-edge data-source="shipping" data-target="mail">Shipping sends the tracking mail</li>
                <li data-edge data-source="payments" data-target="audit">Payments records every charge</li>
                <li data-edge data-source="identity" data-target="audit">Identity records every sign-in</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
</div>

Live. data-graph-curve draws every link: bezier, the default, bends a link away from others between the same records; straight is the shortest line; taxi turns at right angles along the layout's direction; and round-taxi rounds those corners. Right angles suit a hierarchy in boxes, curves a map. The segmented control is data-graph-option="curve".

  • Placed
  • Paid
  • Packed
  • Shipped
  • Delivered
  • Returned
  • Cancelled
  • Refunded
  • An order is paid once the payment is taken
  • A placed order can be cancelled
  • A paid order is picked and packed
  • A paid order that is cancelled is refunded
  • A packed order ships when the carrier takes it
  • A shipped order is delivered once it is signed for
  • A shipped order is returned if it is refused at the door
  • A delivered order is returned when the customer sends it back
  • A returned order is refunded once the goods are checked
<div class="graph-frame" data-graph-frame>
    <div class="toolbar">
        <div class="segmented segmented--sm" role="group" aria-label="How links are drawn">
            <label class="segmented-option">
                <input type="radio" name="layout-curve" value="bezier" data-graph-option="curve" checked /> Curved
            </label>
            <label class="segmented-option">
                <input type="radio" name="layout-curve" value="taxi" data-graph-option="curve" /> Angled
            </label>
            <label class="segmented-option">
                <input type="radio" name="layout-curve" value="round-taxi" data-graph-option="curve" /> Rounded
            </label>
            <label class="segmented-option">
                <input type="radio" name="layout-curve" value="straight" data-graph-option="curve" /> Straight
            </label>
        </div>
    </div>
    <div class="card">
        <div class="graph graph--sm" data-graph id="layout-curves" data-graph-layout="dagre" data-graph-direction="LR"
             data-graph-nodes="box" data-graph-arrows="target" data-graph-spacing="loose"
             aria-label="The states an order passes through">
            <ul class="graph-data" data-graph-data>
                <li data-node="placed" data-tone="1">Placed</li>
                <li data-node="paid" data-tone="1">Paid</li>
                <li data-node="packed" data-tone="1">Packed</li>
                <li data-node="shipped" data-tone="1">Shipped</li>
                <li data-node="delivered" data-tone="go">Delivered</li>
                <li data-node="returned" data-tone="warn">Returned</li>
                <li data-node="cancelled" data-tone="danger">Cancelled</li>
                <li data-node="refunded" data-tone="muted">Refunded</li>
                <li data-edge data-source="placed" data-target="paid" data-label="pay">An order is paid once the payment is taken</li>
                <li data-edge data-source="placed" data-target="cancelled" data-label="cancel">A placed order can be cancelled</li>
                <li data-edge data-source="paid" data-target="packed" data-label="pack">A paid order is picked and packed</li>
                <li data-edge data-source="paid" data-target="refunded" data-label="cancel">A paid order that is cancelled is refunded</li>
                <li data-edge data-source="packed" data-target="shipped" data-label="ship">A packed order ships when the carrier takes it</li>
                <li data-edge data-source="shipped" data-target="delivered" data-label="sign">A shipped order is delivered once it is signed for</li>
                <li data-edge data-source="shipped" data-target="returned" data-label="refuse">A shipped order is returned if it is refused at the door</li>
                <li data-edge data-source="delivered" data-target="returned" data-label="send back">A delivered order is returned when the customer sends it back</li>
                <li data-edge data-source="returned" data-target="refunded" data-label="check">A returned order is refunded once the goods are checked</li>
            </ul>
            <div class="graph-canvas"></div>
            <div class="graph-wait skeleton skeleton-block"></div>
        </div>
    </div>
</div>

Which layout

LayoutFor
forceA map of records, shaped by its links. The default.
islandsA large graph read by its groups: data-group, and data-cluster for groups of groups.
ringsHow far each record is from the root or the focus.
concentricWhat everything goes through: the most connected in the middle.
treeA true tree, breadth-first from the roots, in data-graph-direction.
dagreA layered hierarchy: an org chart, a flow, a dependency tree. A plugin.
fcoseSprings that also lay out groups drawn inside groups, with data-parent. A plugin.
grid, circleEvery record in view, busiest first.
presetWhere data-x and data-y put each record.
any other nameA cytoscape layout of that name, when one is registered on the engine the handle's cy exposes.

A script changes the layout with layout(name, { direction, spacing }) on the handle sednaUi.graph.get() returns, and C# with ISednaGraphs.LayoutAsync.