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.
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.
Use Livebook-friendly nouns and verbs for fast sketches, or the stable pipe-first builders for explicit programmatic models.
Embed architecture, dataflow, workflow, requirements, and threat models into a larger system graph with traceable cross-model relationships.
Slice a model for the conversation you are having: zoom levels, focused neighborhoods, dependency paths, and collapsed subsystems.
Render to Mermaid or Graphviz, open interactive Livebook tabs, export artifacts from `.choreo.exs`, and run domain analysis.
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 |
Switch between fast Lab DSL sketches and the stable pipe-first API. Both styles produce ordinary Choreo models and the same rendered output.
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)
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 ✓")
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)
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")
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) # => []
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)
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)
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
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
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
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
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)
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
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)
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)
The Lab layer is optimized for fast human modeling: concise DSLs, model composition, exploratory views, and interactive renderers.
Livebook-friendly nouns and verbs for C4, Dataflow, ERD, Workflow, Domain, ThreatModel, Requirements, and more.
A compact reference for edge forms, diagram vocabularies, copy-paste snippets, Compose helpers, and View helpers.
Embed separate models into one graph, connect nodes across model boundaries, and add trace relationships.
Focus, trace, path, zoom, collapse, and render diagrams directly from Livebook-friendly helper functions.
A Mermaid renderer for Livebook with pan, zoom, fit-to-screen controls, and notebook-friendly themes.
Converts Mermaid diagrams into editable Excalidraw whiteboards so teams can annotate and iterate visually.
The website gives you a quick tour; the Livebooks are the hands-on path. Open them in GitHub or run them locally in Livebook.
Step-by-step notebooks for C4, dataflow, ERD, FSM, sequence diagrams, workflows, threat models, and more.
Explore Hex dependencies, GitHub issues, Mix xref graphs, Ecto schemas, and Finitomata state machines.
Learn how to add custom diagram DSLs, renderers, protocol implementations, and analysis modules.
Larger examples that combine architecture, behavior, data, infrastructure, and analysis views in one system model.
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.
# 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)
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)
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?”
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)
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.
| 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 |
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.
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)
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.
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)
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? |
Each model targets a specific domain design need with its own semantic graph, analysis functions, and rendering path.
Hierarchical modeling of software systems, containers, and components following the C4 layout specification.
Model dataflow processing graphs, transformation nodes, message routers, and parallel sources/sinks.
Evaluate complex branching heuristics and run entropy validation checks for logical soundness.
Analyze acyclic package dependencies, detect cyclic loops, and optimize layering schedules.
Map strategic bounded contexts, aggregates, entities, and event storming command-policy flows.
Database entity-relationship modeling supporting primary/foreign keys, types, and schema links.
Model VPCs, subnets, load balancers, compute nodes, managed databases, and public/private topology rules.
Model finite state machines with deterministic transitions, dead-state reachability, and path analysis.
Organize concepts hierarchically into parent-child visual layouts representing domain trees.
Schedule tasks, resolve priority constraints, and isolate critical path dependencies.
Trace stakeholder needs to components, tests, refinements, dependencies, and verification coverage.
Map sequence interactions and timelines with sync/async activations and loop/alt conditions.
Map secure boundaries, trust barriers, assets, dataflows, and calculate threat levels statically.
Generate object-oriented class structure layouts mapping structs, schemas, and associations.
Orchestrate tasks, parallel branches, latency weights, Saga compensation hooks, and bottlenecks.
Add Choreo to your mix.exs and start
building visual models of your domain.
def deps do
[
{:choreo, "~> 0.12"}
]
end