Choreo.Domain (Choreo v0.11.0)

Copy Markdown View Source

Domain-Driven Design (DDD) and Event Storming domain-modeling preset for Choreo.

Choreo.Domain is a domain-specific vocabulary layer that adds DDD concepts — Bounded Contexts, Aggregates, Commands, Domain Events, Policies, and Workflows — on top of Choreo's underlying graph and rendering engine.

It draws inspiration from Scott Wlaschin's Domain Modeling Made Functional and classic tactical/strategic DDD patterns.

Tactical Node Types (Event Storming & Workflows)

  • :actor - The user or external trigger initiating a command.
  • :command - An action requested (blue sticky note).
  • :aggregate - Consistency boundary / entity (yellow sticky note).
  • :event - Business domain event (orange sticky note).
  • :read_model - Projection / dashboard (green sticky note).
  • :policy - Saga or reaction policy (purple/lilac sticky note).
  • :external_system - Third-party system boundary (red/rose).
  • :type - Algebraic data type (Slate table card displaying field names and types).
  • :workflow - Wlaschin-style pipeline action (Teal rounded box).
  • :acl - Anti-Corruption Layer translation gateway.

Strategic Modeling (Context Mapping)

Summary

Functions

Adds an Anti-Corruption Layer translation gateway component.

Adds a user or actor initiating a command.

Adds an Aggregate/Entity boundary node.

Adds a Command node (actions requested).

Adds a Bounded Context node for high-level Strategic Context Maps.

Adds a Bounded Context cluster boundary for Tactical design layouts.

Adds a Domain Event node.

Adds an External System node.

Adds a Policy / Saga handler node.

Adds a Read Model / Projection node.

Adds a named business scenario as an ordered domain path.

Adds an Algebraic Data Type node.

Adds a Workflow pipeline function node.

Returns all ancestor causes of a target node.

Clears the current scenario highlight.

Connects two domain nodes.

Connects two strategic Bounded Context nodes with DDD relationship semantics.

Returns all edges in the domain model.

Connects an aggregate, workflow, or external system to an emitted domain event.

Focuses the diagram on a specific execution path scenario, highlighting it while leaving the rest of the diagram visible.

Focuses the diagram on a named scenario path.

Connects a command to the aggregate or workflow that handles it.

Connects an actor, UI, or external trigger to a command.

Initializes a new empty domain model.

Returns all node definitions in the domain model.

Connects an event to an actor/user notification.

Connects an event to a read model/projection that it updates.

Returns a named scenario, or nil when it is not present.

Returns all named scenarios.

Renders the domain model to DOT format.

Renders the domain model to Mermaid.js native syntax.

Connects an upstream model/system to a downstream model/system through an ACL or translation gateway.

Connects an event or policy to a command, policy, workflow, or other reaction.

Types

t()

@type t() :: %Choreo.Domain{
  clusters: %{required(String.t()) => map()},
  edge_meta: %{optional(Yog.Multi.Graph.edge_id()) => map()},
  graph: Yog.Multi.Graph.t(),
  highlighted_edges: [
    Yog.Multi.Graph.edge_id() | {Yog.node_id(), Yog.node_id()}
  ],
  highlighted_nodes: [Yog.node_id()],
  scenarios: %{required(atom()) => map()}
}

Functions

add_acl(domain, id, opts \\ [])

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

Adds an Anti-Corruption Layer translation gateway component.

add_actor(domain, id, opts \\ [])

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

Adds a user or actor initiating a command.

add_aggregate(domain, id, opts \\ [])

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

Adds an Aggregate/Entity boundary node.

add_command(domain, id, opts \\ [])

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

Adds a Command node (actions requested).

add_context(domain, id, opts \\ [])

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

Adds a Bounded Context node for high-level Strategic Context Maps.

add_context_boundary(domain, name, opts \\ [])

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

Adds a Bounded Context cluster boundary for Tactical design layouts.

add_event(domain, id, opts \\ [])

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

Adds a Domain Event node.

add_external_system(domain, id, opts \\ [])

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

Adds an External System node.

add_policy(domain, id, opts \\ [])

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

Adds a Policy / Saga handler node.

add_read_model(domain, id, opts \\ [])

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

Adds a Read Model / Projection node.

add_scenario(domain, name, opts)

@spec add_scenario(t(), atom(), keyword()) :: t()

Adds a named business scenario as an ordered domain path.

Scenarios are useful for documenting use cases and rendering a selected path as a Mermaid eventmodeling timeline.

add_type(domain, id, opts \\ [])

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

Adds an Algebraic Data Type node.

add_workflow(domain, id, opts \\ [])

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

Adds a Workflow pipeline function node.

causes(domain, target)

@spec causes(t(), Yog.node_id()) :: [Yog.node_id()]

Returns all ancestor causes of a target node.

This is a set, not an ordered path — for branching cause graphs (DAGs with multiple parents), all ancestors are returned collapsed into one list with no path structure.

Examples

iex> domain = Choreo.Domain.new()
...>   |> Choreo.Domain.add_actor(:customer)
...>   |> Choreo.Domain.add_command(:place_order)
...>   |> Choreo.Domain.add_aggregate(:order_agg)
...>   |> Choreo.Domain.add_event(:order_placed)
...>   |> Choreo.Domain.connect(:customer, :place_order)
...>   |> Choreo.Domain.connect(:place_order, :order_agg)
...>   |> Choreo.Domain.connect(:order_agg, :order_placed)
iex> ancestors = Choreo.Domain.causes(domain, :order_placed)
iex> :customer in ancestors
true
iex> :order_agg in ancestors
true

clear_focus(domain)

@spec clear_focus(t()) :: t()

Clears the current scenario highlight.

Examples

iex> domain = Choreo.Domain.new()
...>   |> Choreo.Domain.add_actor(:customer)
...>   |> Choreo.Domain.add_command(:place_order)
...>   |> Choreo.Domain.focus_path([:customer, :place_order])
...>   |> Choreo.Domain.clear_focus()
iex> domain.highlighted_nodes
[]
iex> domain.highlighted_edges
[]

connect(domain, from, to, opts \\ [])

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

Connects two domain nodes.

Prefer the semantic helpers (initiates/4, handles/4, emits/4, triggers/4, projects_to/4, notifies/4, and translates_via/4) when the edge has DDD/Event Modeling meaning. Use connect/4 as the generic escape hatch.

connect_contexts(domain, upstream_id, downstream_id, opts \\ [])

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

Connects two strategic Bounded Context nodes with DDD relationship semantics.

edges(domain)

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

Returns all edges in the domain model.

emits(domain, emitter_id, event_id, opts \\ [])

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

Connects an aggregate, workflow, or external system to an emitted domain event.

focus_path(domain, path)

@spec focus_path(t(), [Yog.node_id()]) :: t()

Focuses the diagram on a specific execution path scenario, highlighting it while leaving the rest of the diagram visible.

focus_scenario(domain, name)

@spec focus_scenario(t(), atom()) :: t()

Focuses the diagram on a named scenario path.

handles(domain, command_id, handler_id, opts \\ [])

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

Connects a command to the aggregate or workflow that handles it.

initiates(domain, trigger_id, command_id, opts \\ [])

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

Connects an actor, UI, or external trigger to a command.

new(opts \\ [])

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

Initializes a new empty domain model.

nodes(domain)

@spec nodes(t()) :: %{required(Yog.node_id()) => map()}

Returns all node definitions in the domain model.

notifies(domain, event_id, actor_id, opts \\ [])

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

Connects an event to an actor/user notification.

projects_to(domain, event_id, read_model_id, opts \\ [])

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

Connects an event to a read model/projection that it updates.

scenario(domain, name)

@spec scenario(t(), atom()) :: map() | nil

Returns a named scenario, or nil when it is not present.

scenarios(domain)

@spec scenarios(t()) :: %{required(atom()) => map()}

Returns all named scenarios.

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

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

Returns a theme for Choreo.Domain.

Examples

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

to_dot(domain, opts \\ [])

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

Renders the domain model to DOT format.

to_mermaid(domain, opts \\ [])

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

Renders the domain model to Mermaid.js native syntax.

Options

  • :syntax - :flowchart (default), :class_diagram, :erd, or :event_modeling
  • :path - ordered node IDs for :event_modeling timeline rendering
  • :scenario - named scenario to render with :event_modeling
  • Other options matching the selected syntax engine

translates_via(domain, from_id, to_id, opts \\ [])

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

Connects an upstream model/system to a downstream model/system through an ACL or translation gateway.

triggers(domain, cause_id, effect_id, opts \\ [])

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

Connects an event or policy to a command, policy, workflow, or other reaction.