I’d love to share Choreo — a code-first toolkit designed to help map, visualize, and run metrics on your system architectures.
Choreo embraces the idea of architecture as data. You describe your workflows, pipelines, or data paths declaratively in Elixir, and Choreo builds them up using Yog underneath.
You get both customizable Graphviz DOT layouts, and utilize the built-in architectural validation and analysis.
Supported domains out of the box:
- Finite State Machines (
Choreo.FSM): Trace state transitions, detect terminal dead-ends, and identify unreachable states.
- Workflows (
Choreo.Workflow): Map out critical execution paths, identify latency bottlenecks, and verify Saga pattern constraints (e.g. ensuring compensation paths safely reach termination targets).
- Data Pipelines (
Choreo.Dataflow): Simulate backpressure scenarios, measure stage constraints, and flag unhandled error branches.
- Dependency Trees (
Choreo.Dependency): Unpack cyclic bottlenecks, clean transitive bloat, and compute Uncle Bob’s structural Instability Metrics.
- Threat Models (
Choreo.ThreatModel): Run stride evaluations automatically over mapped vectors.
Quick Example:
alias Choreo.FSM
fsm =
FSM.new()
|> FSM.add_state(:cart, initial: true)
|> FSM.add_state(:processing)
|> FSM.add_state(:completed, final: true)
|> FSM.add_state(:failed, final: true)
|> FSM.transition(:cart, :processing, "checkout")
|> FSM.transition(:processing, :completed, "success")
|> FSM.transition(:processing, :failed, "error")
# Export stunning diagrams with theming
IO.puts FSM.to_dot(fsm, theme: :dark)
Give the documentation a spin or check the source repo:
https://github.com/code-shoily/choreo
I am actively working with it and bug-fixes will be made, and new features will be added frequently.
3 Likes
I checked FSMs and I wonder what does mean plural initial states? It kinda violates everything I know about FInite Automata.
1 Like
I thought it would make it possible to model NFA too.
But that being said, this does reveal a shortcoming - the function deterministic? would return false if you have multiple initial states, so a MapSet.size(FSM.initial_states(fsm)) == 1 is missing.
Thanks for noticing this.
Added a few LiveBooks here.
The documentation still needs a lot of improvements. And I will be trying to learn from Mingrammer and pytm and see if I can update any features or non-trivial cases.
Large diagram generation sucks though.
I am removing support for NFAs and staying within DFA. My trying to avoid epsilon transitions added more headaches than otherwise.
Thank you again for making me rethink this.
Off-topic but just checked Cure. Wow. I’ll talk more on that channel.
1 Like
Here is a bunch of LiveBooks I added demonstrating different architecture diagrams
I tried out Finitomata yesterday. A brilliant project. I was curious if I could use Choreo + Finitomata somehow. So was playing around a bit and got this Livebook. This should run as-is if you have runbook and helped me appreciate the project more!
Thanks for inspiring this @mudasobwa
1 Like
Amazing!
1.3 — Run Analysis Before Deploying → Something like this happens to appear in Finitomata, too; I have to compare what we do check
1.4 — Generate Test Cases → Technically, Finitomata can do it itself, see Finitomata.ExUnit — Finitomata v0.41.0
4.1 — A Finitomata With Problems → I have to add this (specifically, taking into accounts that loops are there already Finitomata.Transition — Finitomata v0.41.0 )
5.2 — Minimize → this does not look 100% reliable to me, because of possible side effects, transitions might have
Part 7: Dynamic Runtime Monitoring with Choreo Heatmapsb → 
Sidenote: why do you Process.sleep/1 everywhere? It’s non-idiomatic and makes not much sense. Mailbox guarantees the proper order, if you need to synch at any moment, just call state/2.
1 Like
Spot on with all your points! This is by no means a serious livebook, just an excuse to explore the API and checking out if I could test out the new analyses functions I came up with after lurking your blog. 
I’ll update the guide, especialy the noisy Process.sleep-s.
Thank you.
1 Like
Added a DSL for easily generating diagrams.
For example the following two snippets are the same:
alias Choreo.Dependency
deps =
Dependency.new()
|> Dependency.add_module(:core, label: "Core Business", layer: :domain)
|> Dependency.add_module(:accounts, label: "Accounts", layer: :domain)
|> Dependency.add_module(:orders, label: "Orders", layer: :domain)
|> Dependency.add_module(:auth, label: "Auth", layer: :application)
|> Dependency.add_module(:repo, label: "Repo", layer: :infrastructure)
|> Dependency.add_module(:mailer, label: "Mailer", layer: :infrastructure)
|> Dependency.depends_on(:accounts, :core)
|> Dependency.depends_on(:orders, :core)
|> Dependency.depends_on(:orders, :accounts)
|> Dependency.depends_on(:auth, :accounts)
|> Dependency.depends_on(:repo, :core)
|> Dependency.depends_on(:mailer, :orders)
# Analysis
Dependency.Analysis.cyclic_dependencies(deps) # => []
Dependency.Analysis.affected_by(deps, :core) # all dependents
AND
alias Choreo.Lab.DSL.Dependency, as: DependencyDSL
deps =
DependencyDSL.dependency do
cluster "Domain" do
core = library("Core Business")
accounts = library("Accounts")
orders = library("Orders")
end
cluster "Application" do
auth = application("Auth")
end
cluster "Infrastructure" do
repo = package("Repo")
mailer = package("Mailer")
end
accounts ~> core |> depends_on("domain logic")
orders ~> core |> depends_on("domain logic")
orders ~> accounts |> calls("account state")
auth ~> accounts |> calls("identity")
repo ~> core |> implements("persistence")
mailer ~> orders |> depends_on("order events")
end
Polished the example livebook guides, and used more DSL syntax with a cheatsheet of each.
The website is also updated.
1 Like