Real examples of Graphviz

January 16, 2020

This week we wanted to highlight graphviz.

Grpahviz diagram

What are main reasons to use Graphviz/DOT:

  1. diagrams as code:
    • easy to review in pull requests.
    • can be embed in your documentation. Supported by sphinx and many others.
    • track the history of changes
    • very simple and intuitive DSL to create by hands
  2. very easy to create programmatically:
    • helper libraries in most of the programming languages (python, javascript, ruby, java you name it)
    • DSL is so easy, so you don't even need a library to create it in your script.

In this article I will show several use cases I personally use Graphviz for.

  1. business flow diagram
  2. microservices interaction
  3. software architecture diagram
  4. workflow diagram
  5. entity relationship diagram

Business Process Diagram

Business process diagram (or BPMN) is a diagram describing business process.

If you are working with DDD (domain driven design) or work with enterprise software you probably read or even write them.

Example:

digraph BPMN {
  rankdir=LR;
  node[shape=rectanble style="rounded,filled" color="lightgoldenrodyellow" ]
  start[shape=circle color=palegreen1]
  end[shape=doublecircle color=orangered]
  join, join2[shape=diamond label="" style="filled" fillcolor=orange]
  wait, delay[shape=circle color="lightgoldenrod1"]
  start -> announce
  announce -> delay
  announce -> moderate
  moderate -> join2
  delay -> email
  email->join2
  check_cal[label="check\ncalendar"]
  announce -> check_cal
  conf_call[label="conference\ncall"]
  check_cal -> conf_call
  conf_call -> wait[label=yes]
  moderate_conf[label="moderate \nconference call"]
  wait -> moderate_conf
  moderate_conf -> join
  conf_call -> join[label=no]
  join->join2
  join2->evaluate
  evaluate->end
}
BPMN start start announce announce start->announce end end join join2 join->join2 evaluate evaluate join2->evaluate wait wait moderate_conf moderate conference call wait->moderate_conf delay delay email email delay->email announce->delay moderate moderate announce->moderate check_cal check calendar announce->check_cal moderate->join2 email->join2 conf_call conference call check_cal->conf_call conf_call->join no conf_call->wait yes moderate_conf->join evaluate->end

(you can play with business process example here)


Microservices Interaction

When you operate in a microservice environment sometimes you need to visualize what's going on.

There are some sub-types of the interactions you care about.

  1. general picture of the service dependencies. With thousands of service running in production sometimes it's hard to understand the dependencies between them. Visual picture is great for that.
  2. you can overlay number of requests on top of #1 above, so you see fanout and how traffic is distributed to microservices.
  3. visualizing specific request aka trace. If you use distributed tracing then you might have some built-in solution for this. If you can create your own and visualize it.
  4. add latencies on top of a single trace or p50/p90/p99 on top of aggregate graph.

Here is an example graph with latencies displayed:

digraph ServiceInteraction {
    apiGateway -> usersService[color=green label="34ms" fontcolor="green"]
    apiGateway -> organizationService[color=red label="552ms" fontcolor="red"]
    organizationService->organizationSearch[color=red label="540ms" fontcolor="red"]
    apiGateway -> repoService[color=orange label="220ms" fontcolor="orange"]
    repoService -> starGazerCache[color=orange label="200ms" fontcolor="orange"]
    repoService -> watchersCache[color=green label="15ms" fontcolor="green"]
}
ServiceInteraction apiGateway apiGateway usersService usersService apiGateway->usersService 34ms organizationService organizationService apiGateway->organizationService 552ms repoService repoService apiGateway->repoService 220ms organizationSearch organizationSearch organizationService->organizationSearch 540ms starGazerCache starGazerCache repoService->starGazerCache 200ms watchersCache watchersCache repoService->watchersCache 15ms

(you can play with Microservice Interaction example here)


Software Architecture Diagram

You are probably using LucidChart, Vizio, Google Docs shapes or similar when creating your design documents or RFCs.

Why it's better to use Graphviz?

  1. have diagrams in your code review / pull request.
  2. check in and modify designs during implementation phase to keep design close to implemnentation.
  3. collaborate on design with your colleagues.

Example architecture diagram:

digraph MessageArchitecture {
  messageClient
  messageQueue[shape=rarrow]
  messageBackend[shape=rectanble]  
  messageDB[shape=cylinder]
  userService[shape=rectangle]
  userDB[shape=cylinder]
  pushNotifications[shape=octagon]
  messageNotifier[shape=rectangle]
  
  messageClient -> messageBackend[label=sendMessage]
  messageBackend -> userService
  userService -> userDB
  messageBackend -> messageDB
  messageDB -> messageQueue[label="change data captrure"]
  messageQueue -> messageNotifier
  messageNotifier -> pushNotifications
}
MessageArchitecture messageClient messageClient messageBackend messageBackend messageClient->messageBackend sendMessage messageQueue messageQueue messageNotifier messageNotifier messageQueue->messageNotifier messageDB messageDB messageBackend->messageDB userService userService messageBackend->userService messageDB->messageQueue change data captrure userDB userDB userService->userDB pushNotifications pushNotifications messageNotifier->pushNotifications

(you can play with Architecture Diagram example here)


workflow diagram

There are multiple workflow systems availabe (Amazon SWF, Amazon Step functions, Netflix conductor, Uber Cadence, Camunda etc).

Some of them come with their own visualization tools and some don't.

Many reasons why you might want to build Graphviz representation for your Workflow Diagrams:

  1. check-in with your documentation.
  2. sketch it before implementing at design phase
  3. regenerate it from your workflow code; then this can be peer-reviewed at pull request. sometimes it isn't obvious what's changed in your workflow until you see it visually.
  4. visualize particular workflow execution or aggregate across all instance of a workflow.

Example workflow graph:

digraph BookingWorkflow {
  start[shape=circle]
  end[shape=doublecircle]
  lock_booking[shape=rectangle]
  process_payment[shape=rectangle]
  successful_payment[shape=diamond label="success?"]
  reserve_booking[shape=rectangle]
  release_booking[shape=rectangle]
  
  start -> lock_booking
  lock_booking -> process_payment
  process_payment -> successful_payment
  successful_payment -> release_booking[label=NO]
  successful_payment -> reserve_booking[label=Yes]
  reserve_booking -> send_email
  send_email -> end
  release_booking -> end
}
BookingWorkflow start start lock_booking lock_booking start->lock_booking end end process_payment process_payment lock_booking->process_payment successful_payment success? process_payment->successful_payment reserve_booking reserve_booking successful_payment->reserve_booking Yes release_booking release_booking successful_payment->release_booking NO send_email send_email reserve_booking->send_email release_booking->end send_email->end

(you can play with Workflow Diagram example here)


entity relationship diagram

Where do you need ERD?

  • During design phase to show your entities and relationships between them
  • Visually see existing entities (from your DB tables)

Visually see the entities and relationships between them can be invaluable especially in your documentation or designs.

Representing it as code (graphviz/dot) is great because:

  1. it can be checked into your documentation, code reviewed.
  2. auto-generated from your DB or RPC schema.

There are some open source projects already that help you to build ERD diagrams from protobuf. Most of the DB clients allow you to build visual ERD from DB schema.

Example ERD represented in graphviz:

digraph GitHub {
    graph [
        rankdir = "LR"
    ];     
    node[shape = record];     
    organization[label="Organization | { url | string } | {id | int} "]
    user[label="User | {avatar|string} | {email|string}"]
    repository[label="Repository | {name|string} | {stargazers_count|int}"]
    repository -> user[label=stargazers taillabel=1 headlabel=N dir=both]
    organization -> repository[label=repos taillabel=1 headlabel=N]
    organization -> user[label=members taillabel=1 headlabel=N]
}
workflow organization Organization url string id int user User avatar string email string organization->user members N 1 repository Repository name string stargazers_count int organization->repository repos N 1 repository->user stargazers N 1

(you can play with ERD example here)


This article was originally published in DevToolsDaily blog