Choreo.Requirement (Choreo v0.11.0)

Copy Markdown View Source

Requirements traceability diagram builder on top of Yog.

Choreo.Requirement models software requirements, the components that satisfy them, the tests that verify them, and the stakeholders that own them. It is aimed at software architects who need practical traceability and risk analysis rather than strict SysML compliance.

When to use

Use Choreo.Requirement when you need to:

  • document feature requirements alongside the services and tests that implement them
  • find gaps in test coverage or implementation
  • propagate risk from high-level requirements down to components
  • perform impact analysis before a change

Node types

  • :requirement — a need, feature, or constraint
  • :component — a service, module, or other implementation item
  • :test — a test or verification activity
  • :stakeholder — a person, team, or source of a requirement

Edge types

  • :satisfies — component fulfills a requirement
  • :verifies — test proves a requirement
  • :refines — child requirement elaborates a parent requirement
  • :depends — requirement needs another requirement first
  • :traces — generic traceability link
  • :contains — parent requirement contains a child requirement
  • :derives — requirement is derived from another

Quick start

model =
  Choreo.Requirement.new("Auth v2")
  |> Choreo.Requirement.add_requirement(:mfa,
    id: "REQ-001",
    text: "Users must authenticate with MFA",
    risk: :high
  )
  |> Choreo.Requirement.add_component(:auth_service, label: "Auth Service")
  |> Choreo.Requirement.add_test(:mfa_test, label: "MFA login test")
  |> Choreo.Requirement.satisfies(:auth_service, :mfa)
  |> Choreo.Requirement.verifies(:mfa_test, :mfa)

Choreo.Requirement.to_mermaid(model)
Choreo.Requirement.to_dot(model)

Analysis

# Coverage gaps
Choreo.Requirement.Analysis.coverage(model)

# Risk propagation
Choreo.Requirement.Analysis.high_risk_gaps(model)

# Impact of changing a component
Choreo.Requirement.Analysis.impact_of(model, :auth_service)

Diagram

digraph G { graph [rankdir=TB, splines=spline, nodesep=0.6, ranksep=1.2]; node [shape=ellipse, style=filled, fillcolor="white", fontname="Helvetica", fontsize=12, fontcolor="white"]; edge [color="#64748b", style=solid, fontname="Helvetica", fontsize=10, penwidth=1.0]; mfa [label="REQ-001\nMFA", fillcolor="#f59e0b", shape="box"]; auth_service [label="Auth Service", fillcolor="#3b82f6", shape="roundedbox"]; mfa_test [label="MFA login test", fillcolor="#10b981", shape="stadium"]; auth_service -> mfa [label="satisfies"]; mfa_test -> mfa [label="verifies"]; }

Summary

Functions

Adds a component node.

Adds a requirement node.

Adds a stakeholder node.

Adds a test node.

Returns all component node IDs.

Creates a contains relationship: a parent requirement contains a child.

Creates a depends relationship: a requirement needs another requirement first.

Creates a derives relationship: a requirement is derived from another.

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

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

Returns the diagram name, or nil if not set.

Creates a new empty requirements diagram.

Returns the raw node data for a given node ID.

Returns all node IDs in the diagram.

Creates a refines relationship: a child requirement elaborates a parent.

Creates a custom relationship between two nodes.

Returns all requirement node IDs.

Creates a satisfies relationship: a component fulfills a requirement.

Returns all stakeholder node IDs.

Returns all test node IDs.

Renders the requirements diagram to DOT format.

Returns the raw Yog.Multi.Graph struct underpinning the diagram.

Renders the requirements diagram to Mermaid.js requirementDiagram syntax.

Creates a generic traces relationship.

Creates a verifies relationship: a test proves a requirement.

Types

t()

@type t() :: %Choreo.Requirement{
  edge_meta: %{optional(Yog.Multi.Graph.edge_id()) => map()},
  graph: Yog.Multi.Graph.t(),
  name: String.t() | nil
}

Functions

add_component(req, id, opts \\ [])

@spec add_component(t(), Yog.node_id(), keyword()) :: t()

Adds a component node.

Components are implementation items (services, modules, libraries) that satisfy requirements.

Options

  • :label (String.t/0) - Display label (defaults to the node id).

  • :type (String.t/0) - Free-form type string for components/tests/stakeholders.

  • :docref (String.t/0) - Optional documentation reference.

Examples

iex> req = Choreo.Requirement.new()
...>   |> Choreo.Requirement.add_component(:auth, label: "Auth Service")
iex> Choreo.Requirement.components(req)
[:auth]

add_requirement(req, id, opts \\ [])

@spec add_requirement(t(), Yog.node_id(), keyword()) :: t()

Adds a requirement node.

Options

  • :id (String.t/0) - Required. Human-readable requirement identifier.

  • :text (String.t/0) - Required. Requirement description.

  • :risk - Risk level: :low, :medium, :high, or :critical. The default value is :medium.

  • :verification - Verification method: :analysis, :inspection, :test, or :demonstration. The default value is :test.

  • :kind - Requirement kind: :requirement, :functional, :interface, :performance, :physical, or :design_constraint. The default value is :requirement.

Examples

iex> req = Choreo.Requirement.new()
...>   |> Choreo.Requirement.add_requirement(:mfa,
...>     id: "REQ-001",
...>     text: "Users must authenticate with MFA",
...>     risk: :high
...>   )
iex> Choreo.Requirement.node(req, :mfa).id
"REQ-001"
iex> Choreo.Requirement.node(req, :mfa).risk
:high

add_stakeholder(req, id, opts \\ [])

@spec add_stakeholder(t(), Yog.node_id(), keyword()) :: t()

Adds a stakeholder node.

Stakeholders are people, teams, or sources of requirements.

Options

  • :label (String.t/0) - Display label (defaults to the node id).

  • :type (String.t/0) - Free-form type string for components/tests/stakeholders.

  • :docref (String.t/0) - Optional documentation reference.

Examples

iex> req = Choreo.Requirement.new()
...>   |> Choreo.Requirement.add_stakeholder(:security, label: "Security Team")
iex> Choreo.Requirement.stakeholders(req)
[:security]

add_test(req, id, opts \\ [])

@spec add_test(t(), Yog.node_id(), keyword()) :: t()

Adds a test node.

Tests verify requirements.

Options

  • :label (String.t/0) - Display label (defaults to the node id).

  • :type (String.t/0) - Free-form type string for components/tests/stakeholders.

  • :docref (String.t/0) - Optional documentation reference.

Examples

iex> req = Choreo.Requirement.new()
...>   |> Choreo.Requirement.add_test(:t1, label: "Login test")
iex> Choreo.Requirement.tests(req)
[:t1]

components(requirement)

@spec components(t()) :: [Yog.node_id()]

Returns all component node IDs.

contains(req, parent, child, opts \\ [])

@spec contains(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a contains relationship: a parent requirement contains a child.

depends(req, requirement, prerequisite, opts \\ [])

@spec depends(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a depends relationship: a requirement needs another requirement first.

derives(req, derived, source, opts \\ [])

@spec derives(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a derives relationship: a requirement is derived from another.

edges(requirement)

@spec edges(t()) :: [{Yog.node_id(), Yog.node_id(), number()}]

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

edges_with_meta(requirement)

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

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

name(requirement)

@spec name(t()) :: String.t() | nil

Returns the diagram name, or nil if not set.

new(name_or_opts \\ [])

@spec new(keyword() | String.t() | nil) :: t()

Creates a new empty requirements diagram.

Accepts an optional name string or keyword options.

Examples

iex> req = Choreo.Requirement.new("Auth v2")
iex> req.name
"Auth v2"
iex> req.graph.kind
:directed

node(requirement, id)

@spec node(t(), Yog.node_id()) :: map() | nil

Returns the raw node data for a given node ID.

nodes(requirement)

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

Returns all node IDs in the diagram.

refines(req, child, parent, opts \\ [])

@spec refines(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a refines relationship: a child requirement elaborates a parent.

relate(req, from, to, opts \\ [])

@spec relate(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a custom relationship between two nodes.

Options

  • :type (atom/0) - Relationship type. The default value is :traces.

  • :label (String.t/0) - Optional edge label used in DOT rendering.

Examples

iex> req = Choreo.Requirement.new()
...>   |> Choreo.Requirement.add_component(:auth)
...>   |> Choreo.Requirement.add_requirement(:mfa, id: "REQ-001", text: "MFA")
...>   |> Choreo.Requirement.relate(:auth, :mfa, type: :implements)
iex> meta = req.edge_meta |> Map.values() |> List.first()
iex> meta.type
:implements

requirements(requirement)

@spec requirements(t()) :: [Yog.node_id()]

Returns all requirement node IDs.

satisfies(req, component, requirement, opts \\ [])

@spec satisfies(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a satisfies relationship: a component fulfills a requirement.

Examples

iex> req = Choreo.Requirement.new()
...>   |> Choreo.Requirement.add_component(:auth)
...>   |> Choreo.Requirement.add_requirement(:mfa, id: "REQ-001", text: "MFA")
...>   |> Choreo.Requirement.satisfies(:auth, :mfa)
iex> [{:auth, :mfa, _}] = Choreo.Requirement.edges(req)

stakeholders(requirement)

@spec stakeholders(t()) :: [Yog.node_id()]

Returns all stakeholder node IDs.

tests(requirement)

@spec tests(t()) :: [Yog.node_id()]

Returns all test node IDs.

theme(name \\ :default, overrides \\ [])

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

Returns a theme for Choreo.Requirement diagrams.

to_dot(req, opts \\ [])

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

Renders the requirements diagram to DOT format.

Options

  • :theme:default, :dark, :warm, :forest, :ocean, or a Choreo.Theme struct

Examples

iex> req = Choreo.Requirement.new() |> Choreo.Requirement.add_requirement(:a, id: "R1", text: "A")
iex> dot = Choreo.Requirement.to_dot(req)
iex> String.contains?(dot, "digraph")
true

to_graph(requirement)

@spec to_graph(t()) :: Yog.Multi.Graph.t()

Returns the raw Yog.Multi.Graph struct underpinning the diagram.

to_mermaid(req, opts \\ [])

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

Renders the requirements diagram to Mermaid.js requirementDiagram syntax.

Options

  • :direction:td (default), :lr, :rl, :bt
  • :theme — used only for styling hints where supported

Examples

iex> req = Choreo.Requirement.new() |> Choreo.Requirement.add_requirement(:a, id: "R1", text: "A")
iex> mermaid = Choreo.Requirement.to_mermaid(req)
iex> String.contains?(mermaid, "requirementDiagram")
true

traces(req, from, to, opts \\ [])

@spec traces(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a generic traces relationship.

verifies(req, test, requirement, opts \\ [])

@spec verifies(t(), Yog.node_id(), Yog.node_id(), keyword()) :: t()

Creates a verifies relationship: a test proves a requirement.