A service in Malla is a module that uses Malla.Service and implements one or more callbacks. Services are the core building block of a Malla application and can run locally or be distributed across a cluster.

By using use Malla.Service, a module gains:

  • Automatic Discovery: Becomes visible to all nodes in the cluster (if global: true).
  • Plugin Architecture: Extensibility through a compile-time callback chain.
  • Status Management: Administrative control (:active, :pause, :inactive).
  • Supervision: A standard supervisor for service-specific children.
  • ETS Storage: A runtime data store that is created on start and destroyed on stop.
  • Observability: Built-in tracing and introspection hooks.

Defining a Service

To define a service, you use Malla.Service in your module and implement your business logic.

defmodule MyService do
  use Malla.Service,
    global: true,
    plugins: [SomePlugin],
    vsn: "1.0.0"

  # A regular function - available for remote calls but not in the callback chain.
  def my_function(arg), do: arg

  # A callback - participates in the plugin chain.
  defcb my_callback(arg) do
    # do work...
    :cont  # or return a value to stop the chain
  end
end

Functions vs. Callbacks

Services expose two types of operations:

  1. Regular Functions (defined with def): Standard Elixir functions that can be called remotely but do not participate in the plugin callback chain.
  2. Callbacks (defined with defcb): Functions that are part of the plugin chain, allowing plugins to intercept, modify, or extend their behavior.

See the Callbacks guide for a detailed explanation.

Service Options

You can configure a service via options in the use Malla.Service macro:

  • :global - (boolean) If true, the service is registered cluster-wide and can be called from any node. Defaults to false.
  • :plugins - (list of modules) A list of plugins to include in the service's callback chain. See Plugins.
  • :vsn - (string) The service version, used for compatibility checking.
  • :otp_app - (atom) Load additional configuration from the specified OTP application's environment.
  • Any other key-value pairs are added to the service's initial configuration. See the Configuration guide.

Service Lifecycle

Services have two independent status dimensions:

  • Admin Status: :active, :pause, :inactive. This is controlled by the administrator/operator.
  • Running Status: :starting, :running, :paused, :stopped, :failed. This is the operational state managed by the system.

For a detailed explanation of the service lifecycle, see the Lifecycle guide.

Starting a Service

You can start a service directly or as part of a supervisor tree.

# Start the service directly
{:ok, pid} = MyService.start_link()

# Start with runtime configuration
{:ok, pid} = MyService.start_link(key: :value)

# Start under a supervisor
children = [
  {MyService, key: :value}
]

Supervisor.start_link(children, strategy: :one_for_one)

Service Storage

Each service instance is provided with its own ETS table for storing runtime data. This table is created when the service starts and destroyed when it stops.

See Storage and State guide for details.

Next Steps