Now v0.12 — Lab DSLs, Compose/View & Livebook-first modeling

Model Systems as Graphs.
Render Them as Diagrams.

Choreo is an Elixir toolkit for modeling architecture, dataflows, workflows, requirements, ERDs, threat models, and domain designs as graph-backed system models — then composing, analyzing, and rendering them with Mermaid, Graphviz, and Livebook.

Lab DSL sketch → graph model → rendered output
Choreo.Lab.DSL.C4
alias Choreo.Lab.DSL.C4, as: C4DSL
alias Choreo.Lab.View

architecture =
  C4DSL.c4 do
    customer = person("Customer")
    api = container("API", technology: "Phoenix")
    db = database("Tenant DB", technology: "Postgres")

    customer ~> api |> uses("HTTPS")
    api ~> db |> reads("SQL")
  end

View.tabs(architecture)
flowchart LR; customer["Customer"]; api["API\nPhoenix"]; db[("Tenant DB\nPostgres")]; customer -- "HTTPS" --> api; api -- "SQL" --> db; style customer fill:#38bdf8,stroke:#0284c7,color:#0c1a2e; style api fill:#10b981,stroke:#059669,color:#fff; style db fill:#8b5cf6,stroke:#6d28d9,color:#fff;

Sketch, compose, analyze, render

Choreo treats diagrams as outputs from reusable graph models, not as one-off drawing files. Start with a domain vocabulary, then use the same model for views, checks, docs, and reviews.

One toolkit, many system views

Pick the vocabulary that matches the design question. Compose the views later when you need an end-to-end system model.

Question Use Typical nouns and verbs
What are the people, systems, containers, and components? C4 person, system, container · uses, calls, reads
How does data move through sources, transforms, queues, and sinks? Dataflow source, transform, sink · emits, writes, dead_letter
What process, Saga, approval, or CI/CD path runs over time? Workflow begin, task, decision, finish · failure, retry
What are the tables, keys, and relationships in the data model? ERD table, pk, fk · one_to_many, many_to_many
Where are trust boundaries, stores, processes, and risky flows? ThreatModel external_entity, process, data_store · sends, encrypted
Which requirements are owned, implemented, verified, and traced? Requirement functional, stakeholder, test_case · satisfies, verifies
How do bounded contexts, commands, aggregates, and events interact? Domain actor, command, aggregate, event · handles, emits

Visual Architectures of Any Type

Switch between fast Lab DSL sketches and the stable pipe-first API. Both styles produce ordinary Choreo models and the same rendered output.

Code style
fsm_example.exs
alias Choreo.FSM

fsm =
  FSM.new()
  |> FSM.add_initial_state(:idle)
  |> FSM.add_state(:processing)
  |> FSM.add_state(:awaiting_payment)
  |> FSM.add_state(:cancelled)
  |> FSM.add_final_state(:completed)
  |> FSM.add_transition(:idle, :processing, label: "submit_order")
  |> FSM.add_transition(:processing, :awaiting_payment, label: "invoice_generated")
  |> FSM.add_transition(:awaiting_payment, :completed, label: "payment_received")
  |> FSM.add_transition(:awaiting_payment, :cancelled, label: "payment_timeout")
  |> FSM.add_transition(:processing, :cancelled, label: "cancel")

# Static analysis
FSM.Analysis.unreachable_states(fsm)     # => []
FSM.Analysis.dead_states(fsm)            # => [:cancelled]
FSM.Analysis.shortest_accepting_path(fsm)
checkout_sequence.exs
alias Choreo.Sequence

seq =
  Sequence.new()
  |> Sequence.add_actor(:user, label: "User")
  |> Sequence.add_participant(:frontend, label: "Frontend")
  |> Sequence.add_participant(:api, label: "API Gateway")
  |> Sequence.add_participant(:payment, label: "Payment Service")
  |> Sequence.add_participant(:db, label: "Database")
  |> Sequence.message(:user, :frontend, label: "Click checkout")
  |> Sequence.message(:frontend, :api, label: "POST /orders")
  |> Sequence.activate(:api)
  |> Sequence.message(:api, :payment, label: "charge_card(amount)")
  |> Sequence.activate(:payment)
  |> Sequence.return(:payment, :api, label: "charge_id")
  |> Sequence.deactivate(:payment)
  |> Sequence.message(:api, :db, label: "INSERT order")
  |> Sequence.return(:db, :api, label: "order_id")
  |> Sequence.deactivate(:api)
  |> Sequence.return(:api, :frontend, label: "201 Created")
  |> Sequence.return(:frontend, :user, label: "Order confirmed ✓")
saga_workflow.exs
alias Choreo.Workflow

workflow =
  Workflow.new()
  |> Workflow.add_start(:order_received)
  |> Workflow.add_task(:validate, label: "Validate Order", timeout_ms: 1000)
  |> Workflow.add_task(:reserve_stock, label: "Reserve Stock", timeout_ms: 3000)
  |> Workflow.add_task(:charge, label: "Charge Card", timeout_ms: 5000)
  |> Workflow.add_task(:ship, label: "Ship Order", timeout_ms: 10_000)
  |> Workflow.add_compensation(:release_stock, for: :reserve_stock)
  |> Workflow.add_compensation(:refund, for: :charge)
  |> Workflow.add_end(:fulfilled)
  |> Workflow.connect(:order_received, :validate)
  |> Workflow.connect(:validate, :reserve_stock)
  |> Workflow.connect(:reserve_stock, :charge)
  |> Workflow.connect(:charge, :ship)
  |> Workflow.connect(:ship, :fulfilled)

# Analysis
Workflow.Analysis.critical_path(workflow)   # latency bottlenecks
Workflow.Analysis.validate(workflow)
shop_schema.exs
alias Choreo.ERD

erd =
  ERD.new()
  |> ERD.add_table(:users,
    columns: [
      %{name: :id, type: :uuid, key: :pk},
      %{name: :email, type: :string},
      %{name: :name, type: :string}
    ]
  )
  |> ERD.add_table(:orders,
    columns: [
      %{name: :id, type: :uuid, key: :pk},
      %{name: :user_id, type: :uuid, key: :fk},
      %{name: :total, type: :decimal}
    ]
  )
  |> ERD.add_table(:line_items,
    columns: [
      %{name: :id, type: :uuid, key: :pk},
      %{name: :order_id, type: :uuid, key: :fk},
      %{name: :product, type: :string},
      %{name: :qty, type: :integer}
    ]
  )
  |> ERD.add_relationship(:users, :orders, cardinality: :one_to_many, label: "places")
  |> ERD.add_relationship(:orders, :line_items, cardinality: :one_to_many, label: "contains")
domain_classes.exs
alias Choreo.UML

uml =
  UML.new()
  |> UML.add_class(:account,
    label: "Account",
    fields: [%{name: :id, type: :uuid}, %{name: :email, type: :string}],
    functions: [%{name: :authenticate, arity: 2}, %{name: :deactivate, arity: 1}]
  )
  |> UML.add_class(:profile, label: "Profile", fields: [%{name: :avatar_url, type: :string}])
  |> UML.add_class(:session, label: "Session", fields: [%{name: :token, type: :string}])
  |> UML.add_class(:audit_log, label: "AuditLog", fields: [%{name: :action, type: :string}])
  |> UML.add_relationship(:account, :profile, type: :associates, label: "has_one")
  |> UML.add_relationship(:account, :session, type: :associates, label: "has_many")
  |> UML.add_relationship(:account, :audit_log, type: :depends, label: "generates")

# Analysis
UML.Analysis.validate(uml)   # => []
payment_c4.exs
alias Choreo.C4

system =
  C4.new()
  |> C4.add_person(:customer, label: "Customer")
  |> C4.add_person(:admin, label: "Admin")
  |> C4.add_software_system(:shop_app, label: "Shop Application", scope: :in)
  |> C4.add_software_system(:payment_gw, label: "Payment Gateway", scope: :out)
  |> C4.add_software_system(:email_svc, label: "Email Service", scope: :out)
  |> C4.add_container(:api, label: "REST API", technology: "Phoenix/Elixir", parent: :shop_app)
  |> C4.add_container(:web, label: "Web UI", technology: "LiveView", parent: :shop_app)
  |> C4.add_container(:db, label: "Database", technology: "PostgreSQL", parent: :shop_app)
  |> C4.add_relationship(:customer, :web, label: "Browses")
  |> C4.add_relationship(:web, :api, label: "HTTP/JSON")
  |> C4.add_relationship(:api, :db, label: "Ecto queries")
  |> C4.add_relationship(:api, :payment_gw, label: "Charge card")
  |> C4.add_relationship(:api, :email_svc, label: "Send receipt")
  |> C4.add_relationship(:admin, :api, label: "Manages")

# Analysis
C4.Analysis.validate(system)    # structural checks
C4.Analysis.missing_technology(system)
elixir_mindmap.exs
alias Choreo.MindMap

map =
  MindMap.new()
  |> MindMap.set_root(:elixir, label: "Elixir")
  |> MindMap.add_topic(:concurrency, label: "Concurrency")
  |> MindMap.add_topic(:ecosystem, label: "Ecosystem")
  |> MindMap.add_topic(:tooling, label: "Tooling")
  |> MindMap.add_subtopic(:processes, label: "Processes")
  |> MindMap.add_subtopic(:genservers, label: "GenServers")
  |> MindMap.add_subtopic(:beam, label: "BEAM VM")
  |> MindMap.add_subtopic(:hex, label: "Hex.pm")
  |> MindMap.add_subtopic(:mix, label: "Mix")
  |> MindMap.add_subtopic(:livebook, label: "Livebook")
  |> MindMap.branch(:elixir, :concurrency)
  |> MindMap.branch(:elixir, :ecosystem)
  |> MindMap.branch(:elixir, :tooling)
  |> MindMap.branch(:concurrency, :processes)
  |> MindMap.branch(:concurrency, :genservers)
  |> MindMap.branch(:ecosystem, :beam)
  |> MindMap.branch(:ecosystem, :hex)
  |> MindMap.branch(:tooling, :mix)
  |> MindMap.branch(:tooling, :livebook)
v1_launch_plan.exs
alias Choreo.Planner

project =
  Planner.new("Launch v0.12")
  |> Planner.add_milestone(:design_complete, title: "Design Complete")
  |> Planner.add_milestone(:beta_release, title: "Beta Release")
  |> Planner.add_task(:wireframes, title: "Wireframes",   status: :done,        estimate_hours: 12)
  |> Planner.add_task(:ui_design,  title: "UI Design",   status: :done,        estimate_hours: 20)
  |> Planner.add_task(:api_impl,   title: "API Impl",    status: :in_progress, estimate_hours: 40)
  |> Planner.add_task(:frontend,   title: "Frontend",    status: :in_progress, estimate_hours: 32)
  |> Planner.add_task(:testing,    title: "QA Testing",  status: :backlog,     estimate_hours: 16)
  |> Planner.add_task(:deploy,     title: "Deploy",      status: :backlog,     estimate_hours: 8)
  |> Planner.depends_on(:frontend, :ui_design)
  |> Planner.depends_on(:api_impl, :wireframes)
  |> Planner.depends_on(:testing, :api_impl)
  |> Planner.depends_on(:testing, :frontend)
  |> Planner.depends_on(:deploy, :testing)

# Analysis
Planner.Analysis.critical_path(project)     # longest dependency chain
Planner.Analysis.ready(project)             # tasks with all deps met
api_threat_model.exs
alias Choreo.ThreatModel

tm =
  ThreatModel.new()
  |> ThreatModel.add_external_entity(:user, label: "End User", boundary: "internet")
  |> ThreatModel.add_external_entity(:attacker, label: "Attacker", boundary: "internet")
  |> ThreatModel.add_process(:web_server, label: "Web Server", boundary: "dmz")
  |> ThreatModel.add_process(:auth_service, label: "Auth Service", boundary: "dmz")
  |> ThreatModel.add_data_store(:user_db, label: "User DB", sensitivity: :restricted)
  |> ThreatModel.add_data_store(:session_cache, label: "Redis Cache", sensitivity: :internal)
  |> ThreatModel.add_trust_boundary("internet", label: "Internet Boundary")
  |> ThreatModel.add_trust_boundary("dmz", label: "DMZ")
  |> ThreatModel.data_flow(:user, :web_server, label: "HTTPS/443", encrypted: true)
  |> ThreatModel.data_flow(:web_server, :auth_service, label: "gRPC", encrypted: true)
  |> ThreatModel.data_flow(:auth_service, :user_db, label: "SQL")
  |> ThreatModel.data_flow(:auth_service, :session_cache, label: "SET/GET")

ThreatModel.Analysis.validate(tm)        # trust-boundary and data-flow checks
ThreatModel.Analysis.stride_threats(tm)  # generated STRIDE findings
module_deps.exs
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
event_pipeline.exs
alias Choreo.Dataflow

pipeline =
  Dataflow.new()
  |> Dataflow.add_source(:kafka,   label: "Kafka Topic")
  |> Dataflow.add_source(:webhook, label: "Webhook")
  |> Dataflow.add_transform(:router,    label: "Event Router")
  |> Dataflow.add_transform(:parser,    label: "JSON Parser")
  |> Dataflow.add_transform(:enricher,  label: "Enricher")
  |> Dataflow.add_transform(:validator, label: "Schema Validator")
  |> Dataflow.add_sink(:postgres, label: "Postgres")
  |> Dataflow.add_sink(:s3,       label: "S3 Archive")
  |> Dataflow.add_sink(:dlq,      label: "Dead Letter Queue")
  |> Dataflow.connect(:kafka, :router)
  |> Dataflow.connect(:webhook, :router)
  |> Dataflow.connect(:router, :parser)
  |> Dataflow.connect(:parser, :enricher)
  |> Dataflow.connect(:enricher, :validator)
  |> Dataflow.connect(:validator, :postgres)
  |> Dataflow.connect(:validator, :s3)
  |> Dataflow.connect(:validator, :dlq, label: "on_error")

Dataflow.Analysis.bottlenecks(pipeline)     # high fan-in stages
Dataflow.Analysis.cyclic?(pipeline)          # false for DAGs
cloud_topology.exs
alias Choreo.Infrastructure

infra =
  Infrastructure.new()
  |> Infrastructure.add_vpc("prod", label: "Production VPC")
  |> Infrastructure.add_subnet_public("public", parent: "prod", label: "Public Subnet")
  |> Infrastructure.add_subnet_private("private", parent: "prod", label: "Private Subnet")
  |> Infrastructure.add_internet(:internet, label: "Internet")
  |> Infrastructure.add_load_balancer(:alb, label: "ALB", cluster: "public")
  |> Infrastructure.add_compute(:api, label: "API Service", cluster: "private")
  |> Infrastructure.add_managed_db(:db, label: "Postgres", cluster: "private")
  |> Infrastructure.connect(:internet, :alb, protocol: :https)
  |> Infrastructure.connect(:alb, :api, protocol: :http)
  |> Infrastructure.connect(:api, :db, protocol: :tcp)

Infrastructure.Analysis.validate(infra)  # topology checks
Infrastructure.to_mermaid(infra, theme: :minimal)
pricing_decision.exs
alias Choreo.DecisionTree

tree =
  DecisionTree.new()
  |> DecisionTree.set_root(:is_premium, feature: "premium?")
  |> DecisionTree.add_decision(:high_usage, feature: "monthly_usage")
  |> DecisionTree.add_decision(:has_discount, feature: "discount_code")
  |> DecisionTree.add_outcome(:enterprise_price, label: "Enterprise Tier", class: "$499/mo")
  |> DecisionTree.add_outcome(:pro_price, label: "Pro Tier", class: "$99/mo")
  |> DecisionTree.add_outcome(:discounted, label: "Discounted", class: "$29/mo")
  |> DecisionTree.add_outcome(:free_tier, label: "Free Tier", class: "$0/mo")
  |> DecisionTree.branch(:is_premium, :high_usage, "yes")
  |> DecisionTree.branch(:is_premium, :has_discount, "no")
  |> DecisionTree.branch(:high_usage, :enterprise_price, "> 10k")
  |> DecisionTree.branch(:high_usage, :pro_price, "<= 10k")
  |> DecisionTree.branch(:has_discount, :discounted, "yes")
  |> DecisionTree.branch(:has_discount, :free_tier, "no")

# Analysis
DecisionTree.Analysis.validate(tree)  # => []
DecisionTree.Analysis.rules(tree)     # IF/THEN paths
checkout_domain.exs
alias Choreo.Domain

domain =
  Domain.new()
  |> Domain.add_actor(:customer, label: "Customer")
  |> Domain.add_command(:place_order, label: "Place Order")
  |> Domain.add_aggregate(:order, label: "Order")
  |> Domain.add_event(:order_placed, label: "Order Placed")
  |> Domain.add_read_model(:order_summary, label: "Order Summary")
  |> Domain.initiates(:customer, :place_order)
  |> Domain.handles(:place_order, :order)
  |> Domain.emits(:order, :order_placed)
  |> Domain.projects_to(:order_placed, :order_summary)

Domain.Analysis.validate(domain)
auth_requirements.exs
alias Choreo.Requirement

reqs =
  Requirement.new("Auth v2")
  |> Requirement.add_stakeholder(:security, label: "Security Team")
  |> Requirement.add_functional(:mfa, id: "REQ-001", text: "Users must authenticate with MFA")
  |> Requirement.add_component(:auth_service, label: "Auth Service")
  |> Requirement.add_test_case(:mfa_login_test, label: "MFA login test")
  |> Requirement.traces(:security, :mfa, label: "owns")
  |> Requirement.satisfies(:auth_service, :mfa, label: "implements")
  |> Requirement.verifies(:mfa_login_test, :mfa, label: "proves")

Requirement.Analysis.coverage(reqs)

Sketch syntax, composition, and Livebook views

The Lab layer is optimized for fast human modeling: concise DSLs, model composition, exploratory views, and interactive renderers.

Run the Interactive Livebooks

The website gives you a quick tour; the Livebooks are the hands-on path. Open them in GitHub or run them locally in Livebook.

Compose Diagrams with embed/4

Nest any Choreo diagram — Dataflow, Workflow, C4, ERD — as a labelled cluster inside a parent system. Nodes are prefixed to avoid ID collisions; edges carry their original metadata.

step 1 — build child diagrams independently
# Each child diagram is a stand-alone Choreo model
ingestion =
  Choreo.Dataflow.new()
  |> Dataflow.add_source(:kafka,    label: "Kafka")
  |> Dataflow.add_transform(:router, label: "Router")
  |> Dataflow.add_sink(:db,         label: "DB Writer")
  |> Dataflow.connect(:kafka, :router)
  |> Dataflow.connect(:router, :db)

orchestration =
  Choreo.Workflow.new()
  |> Workflow.add_start(:trigger)
  |> Workflow.add_task(:process, label: "Process")
  |> Workflow.add_end(:done)
  |> Workflow.connect(:trigger, :process)
  |> Workflow.connect(:process, :done)
step 2 — embed into a parent system
platform =
  Choreo.new()
  # Create named cluster boundaries
  |> Choreo.add_cluster("ingestion",     label: "Data Ingestion")
  |> Choreo.add_cluster("orchestration", label: "Orchestration")
  # Embed with unique prefixes — nodes become :ing_kafka, :orc_trigger …
  |> Choreo.embed(ingestion,     "ingestion",     prefix: "ing_")
  |> Choreo.embed(orchestration, "orchestration", prefix: "orc_")
  # Connect across cluster boundaries
  |> Choreo.connect(:ing_db, :orc_trigger, label: "triggers")

# Render the unified diagram
Choreo.to_mermaid(platform)
rendered output — unified cross-schema diagram

Cross-Diagram Impact Analysis

Declare semantic links between nodes in different diagram schemas. Choreo.trace/4 records a typed relationship (:reads, :writes, :triggers, …) that Choreo.Analysis.Tracing can walk transitively to answer “if this table changes, what workflows break?”

cross_schema_trace.exs
alias Choreo
alias Choreo.Analysis.Tracing

# Embed ERD schema + Workflow into one parent system
platform =
  Choreo.new()
  |> Choreo.add_cluster("schema",   label: "Data Schema")
  |> Choreo.add_cluster("checkout", label: "Checkout Workflow")
  |> Choreo.embed(schema,   "schema",   prefix: "db_")
  |> Choreo.embed(checkout, "checkout", prefix: "wf_")

# Declare semantic cross-diagram traces
platform =
  platform
  |> Choreo.trace(:wf_auth,   :db_users,  type: :reads)
  |> Choreo.trace(:wf_charge, :db_orders, type: :writes)

# Impact analysis — what is downstream of :db_users?
Tracing.impact_analysis(platform, :db_users)
# => [:wf_auth]

# Cross-diagram execution path
Tracing.trace_path(platform, :wf_auth, :db_orders)
# => {:ok, [:wf_auth, :wf_charge, :db_orders]}

# Render with trace arrows visible
Choreo.to_dot(platform, show_traces: true)
what Choreo.trace/4 does

Traces are stored as a separate edge layer — they are invisible by default so they don’t clutter normal diagrams. Pass show_traces: true to any renderer to make them appear as dashed red arrows.

Each trace carries a :type tag (:reads, :writes, :triggers, :depends_on) that the analysis engine uses to build the dependency graph.

analysis functions
Function Returns
impact_analysis/2 All nodes downstream of a change
trace_path/3 Cross-diagram execution path
to_dot/2 show_traces: Renders trace arrows in DOT / Mermaid
rendered trace diagram

Filter Any Diagram by Node Type

Choreo.View.filter/2 keeps only nodes that match a predicate. Use it to remove internals for stakeholder decks, or isolate specific node types across any diagram module.

filter_notes.exs
alias Choreo.MindMap
alias Choreo.View

map =
  MindMap.new()
  |> MindMap.set_root(:elixir, label: "Elixir")
  |> MindMap.add_topic(:concurrency, label: "Concurrency")
  |> MindMap.add_topic(:ecosystem, label: "Ecosystem")
  |> MindMap.add_subtopic(:processes, label: "Processes")
  |> MindMap.add_subtopic(:beam, label: "BEAM VM")
  |> MindMap.add_note(:history, label: "Created 2011")
  |> MindMap.branch(:elixir, :concurrency)
  |> MindMap.branch(:elixir, :ecosystem)
  |> MindMap.branch(:concurrency, :processes)
  |> MindMap.branch(:ecosystem, :beam)

# Remove all notes for a clean stakeholder deck
clean =
  View.filter(map, fn _id, data ->
    data[:node_type] != :note
  end)

MindMap.to_dot(clean)
rendered output — notes hidden

Zoom In and Out of Detail Levels

Choreo.View.zoom/2 filters diagrams by module-defined zoom levels. Each type defines what level 0, 1, 2 … means — so zooming is semantic, not just geometric.

zoom_overview.exs
alias Choreo.MindMap
alias Choreo.View

map =
  MindMap.new()
  |> MindMap.set_root(:elixir, label: "Elixir")
  |> MindMap.add_topic(:concurrency, label: "Concurrency")
  |> MindMap.add_topic(:ecosystem, label: "Ecosystem")
  |> MindMap.add_subtopic(:processes, label: "Processes")
  |> MindMap.add_subtopic(:beam, label: "BEAM VM")
  |> MindMap.add_note(:history, label: "Created 2011")
  |> MindMap.branch(:elixir, :concurrency)
  |> MindMap.branch(:elixir, :ecosystem)
  |> MindMap.branch(:concurrency, :processes)
  |> MindMap.branch(:ecosystem, :beam)

# Zoom out to show only root + topics
overview = View.zoom(map, level: 1)

MindMap.to_dot(overview)
rendered output — zoom level 1

Static Analysis Engine

Choreo acts as a query engine over domain models — detecting architectural issues without running code.

Artifact Modeling Purpose Questions Answered
C4 Model System Architecture Are system boundaries clean? Which systems share databases? What external interfaces exist?
Dataflow Processing Pipelines Where are data bottlenecks? Is backpressure possible? Are processing paths strictly acyclic?
Decision Tree Branching Heuristics Are there duplicate logic paths? Is the decision tree complete? Which branches have missing leaves?
Dependency Package & Module DAGs Are there circular package imports? Which modules are impacted if a low-level module changes?
Domain DDD Strategic & Tactical Design What are aggregate boundaries? Which events trigger policies? Are there unmapped commands?
ERD Database Schema Are column keys consistent? Are there circular entity relationships? Do we have orphaned tables?
Infrastructure Cloud Topology Are databases private? Do internet paths bypass public entrypoints? Are load balancers in the right subnet?
FSM State Transitions Is the state machine deterministic? Are there unreachable or dead-end states? Shortest path to done?
Mind Map Concept Hierarchies Are there disconnected subtopics? Is the structure strictly parent-child? What is the depth distribution?
Planner Task Scheduling What is the project critical path? Which tasks are ready to start? Who has overloaded assignments?
Requirement Traceability Which requirements are covered by components and tests? What changes when a requirement is refined or depends on another?
Sequence Timeline Interactions Are activation boxes balanced? Do async returns match? Is any actor blocked indefinitely?
Threat Model STRIDE Security Which flows cross trust boundaries? Where are sensitive assets stored? What is the static risk level?
UML Class Software Types Which structs reference each other? Are there circular associations or inheritance loops?
Workflow Task Orchestration What is the latency critical path? Which tasks can run in parallel? Are Saga compensations missing?

Specialized Modeling DSLs

Each model targets a specific domain design need with its own semantic graph, analysis functions, and rendering path.

Architecture & Design

C4 Model

Hierarchical modeling of software systems, containers, and components following the C4 layout specification.

Choreo.C4 Systems
Behavior & Flows

Dataflow

Model dataflow processing graphs, transformation nodes, message routers, and parallel sources/sinks.

Choreo.Dataflow Pipelines
Data & Structure

Decision Tree

Evaluate complex branching heuristics and run entropy validation checks for logical soundness.

Choreo.DecisionTree Logic
Data & Structure

Dependency

Analyze acyclic package dependencies, detect cyclic loops, and optimize layering schedules.

Choreo.Dependency DAGs
Architecture & Design

Domain DDD

Map strategic bounded contexts, aggregates, entities, and event storming command-policy flows.

Choreo.Domain DDD
Architecture & Design

ERD

Database entity-relationship modeling supporting primary/foreign keys, types, and schema links.

Choreo.ERD Databases
Architecture & Design

Infrastructure

Model VPCs, subnets, load balancers, compute nodes, managed databases, and public/private topology rules.

Choreo.Infrastructure Cloud
Behavior & Flows

FSM

Model finite state machines with deterministic transitions, dead-state reachability, and path analysis.

Choreo.FSM State
Data & Structure

Mind Map

Organize concepts hierarchically into parent-child visual layouts representing domain trees.

Choreo.MindMap Trees
Data & Structure

Planner

Schedule tasks, resolve priority constraints, and isolate critical path dependencies.

Choreo.Planner PERT
Architecture & Design

Requirement

Trace stakeholder needs to components, tests, refinements, dependencies, and verification coverage.

Choreo.Requirement Traceability
Behavior & Flows

Sequence

Map sequence interactions and timelines with sync/async activations and loop/alt conditions.

Choreo.Sequence Messages
Behavior & Flows

Threat Model

Map secure boundaries, trust barriers, assets, dataflows, and calculate threat levels statically.

Choreo.ThreatModel STRIDE
Architecture & Design

UML Class

Generate object-oriented class structure layouts mapping structs, schemas, and associations.

Choreo.UML UML
Behavior & Flows

Workflow

Orchestrate tasks, parallel branches, latency weights, Saga compensation hooks, and bottlenecks.

Choreo.Workflow Sagas

Install Choreo

Add Choreo to your mix.exs and start building visual models of your domain.

def deps do
  [
    {:choreo, "~> 0.12"}
  ]
end