Skip to content

judepayne/dictim

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Wiki

CI CircleCI Release Latest Release License License: MIT d2 compatibility: 0.7.0

dictim

d2's datafied companion

The dictim project is both a command line tool and clojure library that leverage the underlying power of d2 to transform your data into diagrams.

The command line tool (dict) converts structured data into d2 or beautiful diagrams directly. Generate diagrams from json or edn, parse existing d2 back into data, and automate diagram creation in your workflows.

# Transform data into a diagram in seconds with the '--image' option

dict -i '["app" "Web App"]["db" "Database"] \
         ["app" "->" "db" "queries"]' > diagram.svg

Why dict?

🚀 Diagrams as Data

Generate diagrams programmatically from your data. No more manually updating diagrams when your architecture changes.

Lightning Fast

Native binaries for macOS, Linux, and Windows. No JVM startup time, no dependencies.

🔄 Round-trip Compatible

Parse existing d2 diagrams back into structured data for analysis, transformation, or migration.

🛠 CI/CD Ready

Perfect for documentation pipelines, automated architecture diagrams, and keeping visual documentation in sync with code.

Quick Start

Installation

macOS (Homebrew)

brew install judepayne/tap/dictim

Windows (Scoop)

scoop bucket add judepayne https://github.com/judepayne/scoop-judepayne
scoop install dictim

Note: d2 is installed as a dependency by Homebrew, but is not available on Scoop, so please install it as per the instructions.

Linux/Manual Installation

# Download from releases (amd64)
wget https://github.com/judepayne/dictim/releases/latest/download/dict-linux-amd64
chmod +x dict-linux-amd64
sudo mv dict-linux-amd64 /usr/local/bin/dict

# Or for ARM64
wget https://github.com/judepayne/dictim/releases/latest/download/dict-linux-arm64
chmod +x dict-linux-arm64
sudo mv dict-linux-arm64 /usr/local/bin/dict

Other Platforms

As a babashka uberjar with bbin, please see the Wiki page.

or

download the appropriate release and put on your path.

Check the releases page if none of these options work for you.

Your First Diagram

Create a simple json file:

echo '[
  ["user", "User", {"shape": "person"}],
  ["system", "System", {"shape": "rectangle"}], 
  ["user", "->", "system", "interacts", {"style.stroke": "blue"}]
]' > simple.json

Transform it to d2:

dict -c simple.json

Output:

user: User {shape: person}
system: System {shape: rectangle}
user -> system: interacts {style.stroke: blue}

Render with d2:

dict -c < simple.json | d2 - simple.svg

Or just directly:

dict -i < simple.json > simple.svg

Real-World Examples

Transforming data from a REST API

# Try this now!
curl -s "https://restcountries.com/v3.1/region/europe?fields=name,capital" | \
  jq -r '.[0:6] | map({country: .name.common, capital: (.capital // ["Unknown"])[0]}) | 
         [["Europe", "European Region"]] + 
         (map([.country, .country])) + 
         (map([.capital, .capital])) + 
         (map(["Europe", "->", .country, "contains"])) + 
         (map([.country, "->", .capital, "capital city"])) | @json' | \
  dict -i > europe.svg && open europe.svg

Tip

Point your AI tool to the dictim syntax page and the command line page and it should do great at authoring these little transformer scripts, jq statements. Build up a library!

System architecture

edn

[["frontend" "React Frontend" {"shape" "rectangle"}]
 ["api" "REST API" {"shape" "hexagon"}]
 ["cache" "Redis Cache" {"shape" "cylinder"}]
 ["database" "PostgreSQL" {"shape" "cylinder"}]
 ["frontend" "->" "api" "HTTP requests"]
 ["api" "->" "cache" "cache lookup"]
 ["api" "->" "database" "SQL queries"]]

DevOps Pipeline

Generate deployment diagram from kubernetes infrastructure:

kubectl get services -o json | jq -r '
  .items | map([.metadata.name, .spec.type]) | @json
' | dict -i > deployment.svg

Advanced: Comprehensive microservices architecture

infrastructure.json

[
  ["nginx-ingress", "Ingress Controller", {"shape": "diamond"}],
  ["frontend-service", "React Frontend", {"shape": "rectangle"}],
  ["api-gateway", "API Gateway", {"shape": "hexagon"}],
  ["user-service", "User Service", {"shape": "rectangle"}],
  ["order-service", "Order Service", {"shape": "rectangle"}],
  ["payment-service", "Payment Service", {"shape": "rectangle"}],
  ["notification-service", "Notification Service", {"shape": "rectangle"}],
  ["user-db", "User Database", {"shape": "cylinder"}],
  ["order-db", "Order Database", {"shape": "cylinder"}],
  ["redis-cache", "Redis Cache", {"shape": "cylinder"}],
  ["message-queue", "RabbitMQ", {"shape": "queue"}],
  ["monitoring", "Prometheus", {"shape": "cloud"}],
  ["logging", "ELK Stack", {"shape": "cloud"}],
  ["nginx-ingress", "->", "frontend-service", "routes traffic"],
  ["nginx-ingress", "->", "api-gateway", "API requests"],
  ["frontend-service", "->", "api-gateway", "AJAX calls"],
  ["api-gateway", "->", "user-service", "authenticates"],
  ["api-gateway", "->", "order-service", "order requests"],
  ["api-gateway", "->", "payment-service", "payment processing"],
  ["order-service", "->", "user-service", "validates user"],
  ["order-service", "->", "payment-service", "process payment"],
  ["payment-service", "->", "notification-service", "payment events"],
  ["order-service", "->", "message-queue", "order events"],
  ["notification-service", "<-", "message-queue", "consumes events"],
  ["user-service", "->", "user-db", "stores user data"],
  ["order-service", "->", "order-db", "stores orders"],
  ["user-service", "->", "redis-cache", "caches sessions"],
  ["order-service", "->", "redis-cache", "caches inventory"],
  ["user-service", "->", "monitoring", "metrics"],
  ["order-service", "->", "monitoring", "metrics"],
  ["payment-service", "->", "monitoring", "metrics"],
  ["notification-service", "->", "logging", "logs"]
]

Generate an infrastructure diagram:

dict -i --theme 3 < infrastructure.json > devops-pipeline.svg

Git Branch Analysis

Use a babashka script to transform git output..

git-dict.bb

#!/usr/bin/env bb
(require '[cheshire.core :as json]
         '[clojure.string :as str])

(let [branches (->> (slurp *in*) str/split-lines)
      clean-branches (map #(-> % str/trim (str/replace #"origin/" "")) branches)
      main-branch "main"
      
      ;; Create branch nodes
      branch-nodes (for [branch clean-branches
                         :when (not= branch "HEAD")]
                     (if (= branch main-branch)
                       [branch "Main Branch" {"shape" "hexagon"}]
                       [branch "Feature Branch" {"shape" "rectangle"}]))
      
      ;; Create connections from main to feature branches
      connections (for [branch clean-branches
                        :when (and (not= branch "HEAD") 
                                   (not= branch main-branch))]
                    [main-branch "->" branch "branched from"])
      
      dictim-data (concat '({"direction" "right"}) branch-nodes connections)]
  (println (json/generate-string dictim-data)))

Generate a branch visualization:

git branch -r | bb git-to-dictim.bb | dict -c | d2 - branches.svg

Database Schema

[["objects"
  {"shape" "sql_table"}
  ["id" "int" {"constraint" "primary_key"}]
  ["disk" "int" {"constraint" "foreign_key"}]
  ["json" "jsonb" {"constraint" "unique"}]
  ["last_updated" "timestamp with time zone"]]
 ["disks"
  {"shape" "sql_table"}
  ["id" "int" {"constraint" "primary_key"}]]
 ["objects.disk" "->" "disks.id"]]

Distributed Tracing Sequence Diagrams

Visualize microservice interactions from Jaeger trace data using a babashka script:

jaeger-to-sequence.bb

#!/usr/bin/env bb
(require '[cheshire.core :as json])

(let [jaeger-data (json/parse-string (slurp *in*))
      spans (get-in jaeger-data ["data" 0 "spans"])
      span-map (into {} (map #(vector (get % "spanID") %) spans))
      services (distinct (map #(get-in % ["process" "serviceName"]) spans))
      service-declarations (map #(vector % {"shape" "sequence_diagram"}) services)
      
      interactions
      (for [span spans
            :let [references (get span "references" [])]
            ref references
            :when (= "CHILD_OF" (get ref "refType"))
            :let [parent-span-id (get ref "spanID")
                  parent-span (get span-map parent-span-id)
                  parent-service (get-in parent-span ["process" "serviceName"])
                  child-service (get-in span ["process" "serviceName"])
                  operation (get span "operationName")]]
        [parent-service "->" child-service operation])
      
      ;; Combine sequence diagram declaration, service declarations and interactions  
      dictim-data (concat [["shape" "sequence_diagram"]] service-declarations interactions)]
  (println (json/generate-string dictim-data)))

Generate the sequence diagram:

curl "http://jaeger:16686/api/traces?service=api-gateway&limit=1" | \
  bb jaeger-to-sequence.bb | \
  dict -c | d2 --layout elk - trace.svg

d2 Conversion

Convert existing D2 diagrams to structured data for version control and programmatic manipulation:

api-system.d2

direction: right

# Three-tier architecture
frontend: Web Frontend {
  shape: rectangle
  style.fill: "#87CEEB"
}

api: REST API {
  shape: hexagon
  style.fill: "#98FB98"
}

database: PostgreSQL {
  shape: cylinder
  style.fill: "#FFB6C1"
}

# External user
user: User {shape: person}

# Relationships
user -> frontend: Browser requests
frontend -> api: AJAX calls
api -> database: SQL queries

Parse to structured data:

dict -p api-system.d2 > system.edn

Output: system.edn

({"direction" "right"}
 ["frontend" "Web Frontend" {"shape" "rectangle" "style.fill" "#87CEEB"}]
 ["api" "REST API" {"shape" "hexagon" "style.fill" "#98FB98"}]
 ["database" "PostgreSQL" {"shape" "cylinder" "style.fill" "#FFB6C1"}]
 ["user" "User" {"shape" "person"}]
 ["user" "->" "frontend" "Browser requests"]
 ["frontend" "->" "api" "AJAX calls"]
 ["api" "->" "database" "SQL queries"])

Now you can version control the structured data, apply programmatic transformations, or generate variations:

# Regenerate the diagram
dict -i --theme 4 < system.edn -o updated-system.svg

# Apply a corporate template  
dict -i -t corporate-styles.edn < system.edn -o branded-diagram.svg

Commands

Command Description Example
-c, --compile Transform data to D2 dict -c data.json
-i, --image Transform data directly to SVG diagram dict -i < data.json > diagram.svg
-p, --parse Parse D2 to structured data dict -p diagram.d2
-h, --help Display help information dict --help

Bundled Options:

Command Description Equivalent
-cw Compile and watch -c -w
-pw Parse and watch -p -w
-iw Image and watch -i -w
-aw Apply template and watch -a -w

(and many other commands)

Advanced Usage

Watch Mode for Development

# Auto-regenerate diagram when data changes
dict -cw architecture.json -o arch.d2
# Or using separate flags:
dict -c -w architecture.json -o arch.d2

# Watch and serve the diagram in browser (requires d2)
dict -iw architecture.json

Templates and Theming

# Apply consistent styling across diagrams
dict -c -t corporate-theme.edn system-data.json

Integration with CI/CD

# GitHub Actions example
- name: Generate Architecture Diagrams
  run: |
    dict -c infrastructure.json | d2 - docs/architecture.svg
    git add docs/architecture.svg

Data Format

'dictim syntax' is a datafied version of d2 that stays close to its syntax. It can be either json or edn and is easy to learn:

edn

;; basic shapes and connections
(
[:z]
["a" "A shape"]
["b" "B shape"]
["a" "<->" "b"]
[:z "->" "b" "->" "a" "connection lbl"]

;; shapes can be nested: 'containers'
["ctr" "Group" ["c1" "Aria"]["c2" "Rames"]]

;; everthing can be styled with attributes
[:z "Obj Store" {:shape :cloud :style.fill "red"}]
["a" "<->" "b" "order flows" {"style" (:stroke-dash 5)}]
)

json

[
  ["z"],
  ["a", "A shape"],
  ["b", "B shape"],
  ["a", "<->", "b"],
  ["z", "->", "b", "->", "a", "connection lbl"],
  ["ctr", "Group", ["c1", "Aria"], ["c2", "Rames"]],
  ["z", "Obj Store", {"shape": "cloud", "style.fill": "red"}],
  ["a", "<->", "b", "order flows", {"style": {"stroke-dash": 5}}]
]

See the wiki page for the full syntax.

Documentation

Use as a Clojure/ Babashka library in your app

bb compatible

dict is built on the dictim Clojure library. If you're building Clojure applications, you can use dictim directly:

;; deps.edn
{:deps {io.github.judepayne/dictim {:git/tag "0.9.1" :git/sha "08ff5b3"}}}

Example Use:

(require '[dictim.d2.compile :as d2])

(d2/d2 [["app" "Web App"] 
        ["db" "Database"]
        ["app" "->" "db" "queries"]])
;; => "app: Web App\ndb: Database\napp -> db: queries"

See the Wiki for more details.

Related Projects

See the API Documentation for complete library usage.

License

Copyright © 2025 Jude Payne

Distributed under the MIT License