# `Choreo.Dependency`
[🔗](https://github.com/code-shoily/choreo/blob/v0.14.1/lib/choreo/dependency.ex#L1)

Software dependency graph builder on top of Yog.

`Choreo.Dependency` models component relationships — modules, libraries,
applications, interfaces, and tests — to help visualize and analyze coupling,
layering, and circular dependencies.

## When to use

Use `Choreo.Dependency` when refactoring a codebase, onboarding new
developers, or enforcing architectural boundaries. It surfaces hidden
cycles, measures instability, and identifies the deepest dependency chains
that slow down builds and tests.

## Node types

  * `:application` — deployable service or app
  * `:library` — external or shared library
  * `:module` — internal code module
  * `:interface` — API, contract, or protocol definition
  * `:test` — test suite or spec

## Edge types

  * `:uses` — general dependency
  * `:imports` — explicit import / require
  * `:calls` — runtime function call
  * `:inherits` — inheritance / implementation
  * `:dev` — development-only dependency

## Further reading

  * [Dependency inversion principle](https://en.wikipedia.org/wiki/Dependency_inversion_principle)
  * [Circular dependency](https://en.wikipedia.org/wiki/Circular_dependency)
  * [Coupling (computer programming)](https://en.wikipedia.org/wiki/Coupling_(computer_programming))

## Quick Start

    deps =
      Choreo.Dependency.new()
      |> Choreo.Dependency.add_application(:api, label: "API Gateway")
      |> Choreo.Dependency.add_library(:phoenix, label: "Phoenix")
      |> Choreo.Dependency.add_module(:auth, label: "Auth Module")
      |> Choreo.Dependency.depends_on(:api, :phoenix, type: :uses)
      |> Choreo.Dependency.depends_on(:api, :auth, type: :calls)

    dot = Choreo.Dependency.to_dot(deps)

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    auth [label="Auth Module", fillcolor="#10b981", shape="box"];
    api [label="API Gateway", fillcolor="#3b82f6", shape="box3d"];
    phoenix [label="Phoenix", fillcolor="#f59e0b", shape="cylinder"];

    api -> auth [style="dotted", label="calls"];
    api -> phoenix [label="uses"];
  }
</div>

## Analysis

    # Find circular dependencies
    Choreo.Dependency.Analysis.cyclic_dependencies(deps)
    #=> [[:repo, :service, :repo]]

    # Impact analysis: what breaks if :auth changes?
    Choreo.Dependency.Analysis.affected_by(deps, :auth)
    #=> [:api, :web]

    # Check layer violations
    layers = %{api: 3, web: 2, repo: 1}
    Choreo.Dependency.Analysis.layer_violations(deps, layers)
    #=> [{:repo, :api, "repo (layer 1) calls api (layer 3)"}]

# `t`

```elixir
@type t() :: %Choreo.Dependency{
  clusters: %{required(String.t()) =&gt; map()},
  edge_meta: %{optional(Yog.Multi.Graph.edge_id()) =&gt; map()},
  graph: Yog.Multi.Graph.t(),
  strict: boolean()
}
```

# `add_application`

Adds an application node (deployable service or app).

## Options

* `:label` (`t:String.t/0`)

* `:description` (`t:String.t/0`)

* `:cluster` (`t:String.t/0`)

* `:shape` (`t:atom/0`)

* `:fillcolor` (`t:String.t/0`)

* `:fontcolor` (`t:String.t/0`)

* `:style` (`t:String.t/0`)

* `:penwidth`

* `:image` (`t:String.t/0`)

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = Choreo.Dependency.add_application(deps, :api, label: "API")
    iex> Choreo.Dependency.nodes(deps)
    [:api]
    iex> Map.get(deps.graph.nodes, :api).node_type
    :application
    iex> Map.get(deps.graph.nodes, :api).label
    "API"

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    api [label="API Gateway", fillcolor="#3b82f6", shape="box3d"];
  }
</div>

# `add_cluster`

```elixir
@spec add_cluster(t(), String.t(), keyword()) :: t()
```

Defines a cluster for grouping nodes visually (e.g., by team or layer).

## Options

* `:parent` (`t:String.t/0`)

* `:label` (`t:String.t/0`)

* `:style` (`t:String.t/0`)

* `:fillcolor` (`t:String.t/0`)

* `:color` (`t:String.t/0`)

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = Choreo.Dependency.add_cluster(deps, "core", label: "Core")
    iex> deps.clusters["cluster_core"].label
    "Core"

# `add_interface`

Adds an interface node (API, contract, protocol).

## Options

* `:label` (`t:String.t/0`)

* `:description` (`t:String.t/0`)

* `:cluster` (`t:String.t/0`)

* `:shape` (`t:atom/0`)

* `:fillcolor` (`t:String.t/0`)

* `:fontcolor` (`t:String.t/0`)

* `:style` (`t:String.t/0`)

* `:penwidth`

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = Choreo.Dependency.add_interface(deps, :contract)
    iex> Choreo.Dependency.nodes(deps)
    [:contract]
    iex> Map.get(deps.graph.nodes, :contract).node_type
    :interface

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    contract [label="contract", fillcolor="#8b5cf6", shape="diamond"];
  }
</div>

# `add_library`

Adds a library node (external or shared dependency).

## Options

* `:label` (`t:String.t/0`)

* `:description` (`t:String.t/0`)

* `:cluster` (`t:String.t/0`)

* `:shape` (`t:atom/0`)

* `:fillcolor` (`t:String.t/0`)

* `:fontcolor` (`t:String.t/0`)

* `:style` (`t:String.t/0`)

* `:penwidth`

* `:image` (`t:String.t/0`)

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = Choreo.Dependency.add_library(deps, :phx, label: "Phoenix")
    iex> Choreo.Dependency.nodes(deps)
    [:phx]
    iex> Map.get(deps.graph.nodes, :phx).node_type
    :library

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    phx [label="Phoenix", fillcolor="#f59e0b", shape="cylinder"];
  }
</div>

# `add_module`

Adds a module node (internal code unit).

## Options

* `:label` (`t:String.t/0`)

* `:description` (`t:String.t/0`)

* `:cluster` (`t:String.t/0`)

* `:shape` (`t:atom/0`)

* `:fillcolor` (`t:String.t/0`)

* `:fontcolor` (`t:String.t/0`)

* `:style` (`t:String.t/0`)

* `:penwidth`

* `:image` (`t:String.t/0`)

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = Choreo.Dependency.add_module(deps, :auth)
    iex> Choreo.Dependency.nodes(deps)
    [:auth]
    iex> Map.get(deps.graph.nodes, :auth).node_type
    :module

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    auth [label="auth", fillcolor="#10b981", shape="box"];
  }
</div>

# `add_test`

Adds a test node (test suite or spec).

## Options

* `:label` (`t:String.t/0`)

* `:description` (`t:String.t/0`)

* `:cluster` (`t:String.t/0`)

* `:shape` (`t:atom/0`)

* `:fillcolor` (`t:String.t/0`)

* `:fontcolor` (`t:String.t/0`)

* `:style` (`t:String.t/0`)

* `:penwidth`

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = Choreo.Dependency.add_test(deps, :auth_test)
    iex> Choreo.Dependency.nodes(deps)
    [:auth_test]
    iex> Map.get(deps.graph.nodes, :auth_test).node_type
    :test

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    auth_test [label="auth_test", fillcolor="#64748b", shape="note"];
  }
</div>

# `depends_on`

Creates a dependency edge from one component to another.

Direction reads as "`from` depends on `to`".

Parallel edges are supported: the same `(from, to)` pair can be added
multiple times with different `:type` values (e.g. `:uses` and `:dev`).

## Options

* `:type`

* `:label` (`t:String.t/0`)

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:api)
    ...>   |> Choreo.Dependency.add_module(:auth)
    ...>   |> Choreo.Dependency.depends_on(:api, :auth)
    iex> [{_, _, _, meta}] = Choreo.Dependency.edges_with_meta(deps)
    iex> meta.type
    :uses

## Diagram

<div class="graphviz">
  digraph G {
    graph [rankdir=TB, splines=spline, nodesep=0.5, ranksep=1.0];
    node [shape=box, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"];
    edge [arrowhead=normal, color="#64748b", style=solid, fontname="Helvetica", fontsize=9, penwidth=1.0];

    auth [label="auth", fillcolor="#10b981", shape="box"];
    api [label="api", fillcolor="#3b82f6", shape="box3d"];

    api -> auth [label="uses"];
  }
</div>

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:api)
    ...>   |> Choreo.Dependency.add_module(:auth)
    ...>   |> Choreo.Dependency.depends_on(:api, :auth, type: :calls)
    iex> [{_, _, _, meta}] = Choreo.Dependency.edges_with_meta(deps)
    iex> meta.type
    :calls
    iex> meta.label
    "calls"

# `edges`

```elixir
@spec edges(t()) :: [{Yog.node_id(), Yog.node_id(), integer()}]
```

Returns all dependency edges as `{from, to, weight}` tuples.

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:api)
    ...>   |> Choreo.Dependency.add_module(:auth)
    ...>   |> Choreo.Dependency.depends_on(:api, :auth)
    iex> Choreo.Dependency.edges(deps)
    [{:api, :auth, 1}]

# `edges_with_meta`

```elixir
@spec edges_with_meta(t()) :: [{Yog.node_id(), Yog.node_id(), any(), map()}]
```

Returns all edges with their metadata as `{from, to, weight, meta}` tuples.

# `new`

```elixir
@spec new(keyword()) :: t()
```

Creates a new empty dependency graph.

Dependency graphs are always directed.

## Options

  * `:strict` — if `true`, `depends_on/4` raises when an endpoint does not
    already exist. Default `false` (auto-creates missing nodes as `:module`).

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> Choreo.Dependency.nodes(deps)
    []
    iex> Choreo.Dependency.edges(deps)
    []

# `nodes`

```elixir
@spec nodes(t()) :: [Yog.node_id()]
```

Returns all node IDs in the dependency graph.

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:api)
    ...>   |> Choreo.Dependency.add_module(:auth)
    iex> Enum.sort(Choreo.Dependency.nodes(deps))
    [:api, :auth]

# `nodes_of_type`

```elixir
@spec nodes_of_type(t(), atom()) :: [Yog.node_id()]
```

Returns all nodes of a given type.

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:a)
    ...>   |> Choreo.Dependency.add_application(:b)
    ...>   |> Choreo.Dependency.add_library(:c)
    iex> Enum.sort(Choreo.Dependency.nodes_of_type(deps, :application))
    [:a, :b]
    iex> Choreo.Dependency.nodes_of_type(deps, :library)
    [:c]
    iex> Choreo.Dependency.nodes_of_type(deps, :module)
    []

# `theme`

```elixir
@spec theme(atom(), keyword()) :: Choreo.Theme.t()
```

Returns a theme for `Choreo.Dependency`.

Supported theme names: `:default`, `:dark`, `:minimal`, `:warm`, `:forest`, `:ocean`.

## Examples

    iex> theme = Choreo.Dependency.theme(:default, graph_rankdir: :lr)
    iex> theme.graph_rankdir
    :lr

# `to_dot`

```elixir
@spec to_dot(t(), keyword()) :: String.t()
```

Renders the dependency graph to DOT format.

## Options

  * `:theme` — `:default`, `:dark`, `:minimal`, `:warm`, `:forest`, `:ocean`, or a `Choreo.Theme` struct
  * `:rankdir` — `:tb` (default), `:lr`, etc.
  * `:engine` — Graphviz layout engine (`"dot"`, `"neato"`, `"twopi"`, `"circo"`, `"fdp"`, etc.)
  * `:highlighted_nodes` — list of node IDs to highlight
  * `:highlighted_edges` — list of `{from, to}` tuples or edge IDs to highlight

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:api, label: "API")
    ...>   |> Choreo.Dependency.add_module(:auth, label: "Auth")
    ...>   |> Choreo.Dependency.depends_on(:api, :auth)
    iex> dot = Choreo.Dependency.to_dot(deps)
    iex> String.contains?(dot, "digraph")
    true
    iex> String.contains?(dot, "API")
    true
    iex> String.contains?(dot, "Auth")
    true

# `to_graph`

```elixir
@spec to_graph(t()) :: Yog.graph()
```

Returns the raw `Yog.Graph` struct underpinning the dependency graph.

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> graph = Choreo.Dependency.to_graph(deps)
    iex> graph.kind
    :directed

# `to_mermaid`

```elixir
@spec to_mermaid(t(), keyword()) :: String.t()
```

Renders the dependency graph to Mermaid.js syntax.

## Options

  * `:syntax` — `:flowchart` (default) or `:class_diagram` (native `classDiagram` syntax)
  * `:theme` — `:default`, `:dark`, `:minimal`, `:warm`, `:forest`, `:ocean`, or a `Choreo.Theme` struct (for `:flowchart` syntax)
  * `:direction` — `:td` (default), `:lr`, `:bt`, `:rl`
  * `:highlighted_nodes` — list of node IDs to highlight (for `:flowchart` syntax)
  * `:highlighted_edges` — list of `{from, to}` tuples or edge IDs to highlight (for `:flowchart` syntax)

## Examples

    iex> deps = Choreo.Dependency.new()
    iex> deps = deps
    ...>   |> Choreo.Dependency.add_application(:api, label: "API")
    ...>   |> Choreo.Dependency.add_module(:auth, label: "Auth")
    ...>   |> Choreo.Dependency.depends_on(:api, :auth)
    iex> mermaid = Choreo.Dependency.to_mermaid(deps)
    iex> String.contains?(mermaid, "graph TD")
    true

# `to_simple_graph`

```elixir
@spec to_simple_graph(t(), keyword()) :: Yog.Graph.t()
```

Collapses parallel edges into a simple Graph for algorithm analysis.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
