Choreo.UML.Analysis (Choreo v0.9.0)

Copy Markdown View Source

Architectural analysis suite for Choreo.UML class/struct diagrams.

Provides static analysis tools optimized for functional and OOP software design:

Summary

Functions

Identifies components which have declared a :realizes or :inherits relationship to a :behavior, :protocol, or :interface component but do not implement all the functions specified by that contract.

Computes coupling and stability metrics for all components in the UML diagram.

Identifies all circular dependency paths in the UML diagram.

Identifies all Law of Demeter violations in the UML class diagram.

Functions

broken_contracts(uml)

@spec broken_contracts(Choreo.UML.t()) :: [
  {Choreo.UML.class_id(), Choreo.UML.class_id(), [map()]}
]

Identifies components which have declared a :realizes or :inherits relationship to a :behavior, :protocol, or :interface component but do not implement all the functions specified by that contract.

Returns a list of tuples [{component_id, contract_id, [missing_function_maps]}].

Examples

iex> contract = Choreo.UML.new()
...>   |> Choreo.UML.add_class(:auth, type: :behavior, functions: [%{name: "verify", arity: 1}])
...>   # Struct missing the arity 1 verify function:
...>   |> Choreo.UML.add_class(:provider, type: :struct, functions: [%{name: "verify", arity: 2}])
...>   |> Choreo.UML.add_relationship(:provider, :auth, type: :realizes)
iex> [{:provider, :auth, [missing]}] = Choreo.UML.Analysis.broken_contracts(contract)
iex> missing[:name]
"verify"
iex> missing[:arity]
1

coupling_metrics(uml)

@spec coupling_metrics(Choreo.UML.t()) :: %{
  optional(Choreo.UML.class_id()) => %{
    afferent: non_neg_integer(),
    efferent: non_neg_integer(),
    instability: float()
  }
}

Computes coupling and stability metrics for all components in the UML diagram.

Returns a map of component IDs to:

  • :afferent — number of incoming dependencies (who depends on me)
  • :efferent — number of outgoing dependencies (who do I depend on)
  • :instability — Ce / (Ca + Ce). Stable core modules approach 0.0, unstable leaf modules approach 1.0.

Examples

iex> uml =
...>   Choreo.UML.new()
...>   |> Choreo.UML.add_class(:a)
...>   |> Choreo.UML.add_class(:b)
...>   |> Choreo.UML.add_relationship(:a, :b, type: :associates)
iex> metrics = Choreo.UML.Analysis.coupling_metrics(uml)
iex> metrics[:a]
%{afferent: 0, efferent: 1, instability: 1.0}
iex> metrics[:b]
%{afferent: 1, efferent: 0, instability: 0.0}

cycles(uml)

@spec cycles(Choreo.UML.t()) :: [[Choreo.UML.class_id()]]

Identifies all circular dependency paths in the UML diagram.

Cycles in structural diagrams indicate high coupling and are a major source of compilation cascades in Elixir applications.

Returns a list of cycles, where each cycle is a list of node IDs starting at the canonical smallest node and listing each member once.

Examples

iex> uml =
...>   Choreo.UML.new()
...>   |> Choreo.UML.add_class(:a)
...>   |> Choreo.UML.add_class(:b)
...>   |> Choreo.UML.add_relationship(:a, :b, type: :associates)
...>   |> Choreo.UML.add_relationship(:b, :a, type: :depends)
iex> Choreo.UML.Analysis.cycles(uml)
[[:a, :b]]

law_of_demeter_violations(uml)

@spec law_of_demeter_violations(Choreo.UML.t()) :: [
  {Choreo.UML.class_id(), Choreo.UML.class_id(), Choreo.UML.class_id()}
]

Identifies all Law of Demeter violations in the UML class diagram.

A violation occurs when a class A has a direct relationship to a class C, but also has a path through an intermediate class B (i.e., A -> B, B -> C, and A -> C).

Returns a list of triplets [{class_a, class_b, class_c}] representing the violations.

Examples

iex> uml =
...>   Choreo.UML.new()
...>   |> Choreo.UML.add_class(:a)
...>   |> Choreo.UML.add_class(:b)
...>   |> Choreo.UML.add_class(:c)
...>   |> Choreo.UML.add_relationship(:a, :b, type: :associates)
...>   |> Choreo.UML.add_relationship(:b, :c, type: :associates)
...>   |> Choreo.UML.add_relationship(:a, :c, type: :associates)
iex> Choreo.UML.Analysis.law_of_demeter_violations(uml)
[{:a, :b, :c}]