Skip to main content

Roam

Roam is a systems bootstrapping tool for Roblox luau projects, designed to make it easy to initialize and start services in a topologically sorted manner without the need to manually order and start services.

Roam follows a design pattern similar to Knit, but is more lightweight. It removes all networking and replication functionality, and instead focuses on providing a simple methodology to easily initialize and start Services given to it.

Roam is RunContext agnostic, meaning it can be used on both the server and client in the same manner. It makes no distinction between the two, and instead focuses on providing a simple interface for initializing and starting services. This means you could create a service and register it on both the server and client, and it will be initialized and started on both ends.

[EXAMPLE SERVICE]

-- MyService.lua
local MyService = {}

function MyService:RoamInit()
	print("MyService initialized!")
end

function MyService:RoamStart()
	print("MyService started!")
end

-- Register the service table with Roam
local Roam = require(ReplicatedStorage.Roam)
Roam.registerService(MyService, "MyService")

return MyService

[EXAMPLE STARTUP]

-- ServerBootstrapper.server.lua
local Roam = require(ReplicatedStorage.Roam)

-- Require your services. (Tip: Roam.requireModules can help abstract this process!)
require(ReplicatedStorage.MyService)

-- Start Roam
Roam.start()
:andThenCall(print, "Roam started!")
:catch(warn)

[CONTRACTS]

  • Services must be created/registered before Roam is started.
  • Services must be created/registered with a unique name.
  • Services with RoamInit and RoamStart methods will have those methods called when Roam is started at the appropriate time. (Names are configurable)
  • RequiredServices boot in proper topological order if specified in the ServiceConfig.
  • Roam functions the same regardless of RunContext (Server/Client).
Setting up Services

Services can be set up in a variety of ways. The most common way is to create a ModuleScript that returns a table with the methods you want to define, and then register it with Roam just prior to the final module's return.

See Roam.registerService for more information on setting up a new service.

Networking

Roam does not inherently have networking functionality. However, it can easily be added through the use of NetWire's .setupServiceNetworking function.


LIFECYCLE

For those interested in the full execution order roam follows for booting services, see the diagrams below:

Initialization Phase (Synchronous):

  • GlobalPreInit - Called once before ANY service initializes (global setup)
  • For each service (in dependency order):
    • Await dependencies to finish initializing
    • PreInit(service) - Called before this service's RoamInit
    • Service's RoamInit() method executes
    • PostInit(service) - Called after this service's RoamInit
  • GlobalPostInit - Called once after ALL services finish initializing

Start Phase (Fully Async - Does Not Block):

  • GlobalPreStart - Called once before ANY service starts.
  • For each service (in dependency order):
    • PreStart(service) - Called before this service's RoamStart
    • Service's RoamStart() method executes
    • PostStart(service) - Called after this service's RoamStart
  • GlobalPostStart - Called once after ALL services start

Key Concepts:

  • Parallel Initialization: Services without dependencies initialize concurrently via coroutines
  • Dependency Blocking: Services wait for their dependencies' PostInit before starting their PreInit
  • Synchronous Init Phase: The Initialization Phase completes fully before Start Phase begins
  • Async Start Phase: The Start Phase spawns services lifecycle methods asynchronously

Advanced Roam Lifecycle Diagram

Types

Service

type Service = table

A service is a table that can be registered with Roam.

ServiceConfig

interface ServiceConfig {
Namestring--

Name of the Service. Must be unique. Used when accessing via .getService

RequiredServices{Service}?--

The Services that this Service depends on. Roam will ensure that these Services are initialized before this Service.

StartMethodNamestring?--

Overrides default StartMethodName of "RoamStart"

InitMethodNamestring?--

Overrides default InitMethodName of "RoamInit"

}
local myOtherService = require(ReplicatedStorage.MyOtherService)

-------------------------------------------------

local MyService = {}

function MyService:CustomStartMethod()
	print("MyService started!")
end

-------------------------------------------------

Roam.registerService(MyService, {
	Name = "MyService",
	RequiredServices = {myOtherService},
	StartMethodName = "CustomStartMethod",
})

return MyService
Deferring RequiredServices

Do NOT add services to the RequiredServices after you have created or registered the service. This will cause undefined behavior.

StartConfig

interface StartConfig {
GlobalPreInit(() → ())?--

Called once before ANY service initializes

PreInit((serviceService) → ())?--

Called before each service's RoamInit

PostInit((serviceService) → ())?--

Called after each service's RoamInit

GlobalPostInit(() → ())?--

Called once after ALL services finish initializing

GlobalPreStart(() → ())?--

Called once before ANY service starts. (Async)

PreStart((serviceService) → ())?--

Called before each service's RoamStart (Async)

PostStart((serviceService) → ())?--

Called after each service's RoamStart (Async)

GlobalPostStart(() → ())?--

Called once after ALL services start (Async)

}

Yielding in Init lifecycle hooks will prevent Roam from progressing to the next step.

Start lifecycle hooks are fully asynchronous and do not block progression at any point.

Properties

Bootstrappers

Roam.Bootstrappers: {
Server(scriptScript) → Promise,
Client(scriptScript) → Promise
}

A table of generic bootstrappers for Roam that you can use to quickly setup new projects.

Roam.Bootstrappers.Server(script)
:andThenCall(print, "Roam Server Bootstrapped!")

Functions

registerService

Roam.registerService(
serviceService,
serviceConfig(ServiceConfig | string)?
) → Service

Registers a Service/Table with Roam to be Initialized and Started when Roam starts. Cannot be called after Roam has been started.

local MyService = {}

function MyService:RoamInit()
	print("MyService initialized!")
end

function MyService:RoamStart()
	print("MyService started!")
end

----------------------------------------------------------------

Roam.registerService(MyService, "MyService")

start

Roam.start(configStartConfig?) → Promise

Starts Roam. Should only be called once. Calling multiple times will result in a promise rejection.

Optional config argument provides lifecycle hooks.

Roam.start({
	GlobalPreInit = function()
		print("=== Initialization Phase Starting ===")
	end,
	PreInit = function(service)
		print("Initializing:", Roam.getServiceName(service))
	end,
	PostInit = function(service)
		print("✓ Initialized:", Roam.getServiceName(service))
	end,
	GlobalPostInit = function()
		print("=== All Services Initialized ===")
	end,
})
:andThenCall(print, "Roam started!")
:catch(warn)
CAUTION

Be sure that all services have been created before calling Start. Services cannot be added later.

onStart

Roam.onStart() → Promise

Returns a promise that is resolved once Roam has started. This is useful for any code that needs to tie into Roam services but is not the script that called start.

Roam.onStart():andThen(function()
	local MyService = require(ReplicatedStorage.MyService)
	MyService:DoSomething()
end):catch(warn)

isReady

Roam.isReady() → boolean

Returns whether or not Roam has been successfully started and is ready for external access.

requireModules

Roam.requireModules(
parentsInstance | {Instance},
config{
DeepSearchboolean?,
AllowYieldingRequiresboolean?,
StopOnFailedRequireboolean?,
RequirePredicate((objModuleScript) → boolean)?,
IgnoreDescendantsPredicate((objInstance) → boolean)?,
}?
) → {
Successboolean,
ModuleContent{[ModuleScript]any},
FailedModuleRequires{ModuleScript},
}

Requires all the modules that are children of the given parent. This is an easy way to quickly load all services that might be in a folder. Takes an optional predicate function to filter which modules are loaded. Services collected this way must not yield.

  • DeepSearch -> whether it checks descendants or just children
  • AllowYieldingRequires -> whether to allow required modules to yield (default: false)
  • RequirePredicate -> a predicate function that determines whether a module should be required
  • IgnoreDescendantsPredicate -> A Predicate for whether the Descendants of an instance should be Searched (Only matters if DeepSearch is true)
  • StopOnFailedRequire -> whether to stop requiring modules if one fails to require (default: false). Useful for debugging, helps to clear excessive noise from the output.
local pred = function(obj: ModuleScript): boolean
	return obj.Name:match("Service$") ~= nil
end

Roam.requireModules(ReplicatedStorage.Shared, {
	DeepSearch = true,
	RequirePredicate = pred,
	IgnoreDescendantsPredicate = function(obj: Instance): boolean
		return obj.Name == "Ignore"
	end,
})
Show raw api
{
    "functions": [
        {
            "name": "registerService",
            "desc": "Registers a Service/Table with Roam to be Initialized and Started when Roam starts.\nCannot be called after Roam has been started.\n\n```lua -- MyService.lua\nlocal MyService = {}\n\nfunction MyService:RoamInit()\n\tprint(\"MyService initialized!\")\nend\n\nfunction MyService:RoamStart()\n\tprint(\"MyService started!\")\nend\n\n----------------------------------------------------------------\n\nRoam.registerService(MyService, \"MyService\")\n```",
            "params": [
                {
                    "name": "service",
                    "desc": "",
                    "lua_type": "Service"
                },
                {
                    "name": "serviceConfig",
                    "desc": "",
                    "lua_type": "(ServiceConfig | string)?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Service\n"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 489,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "start",
            "desc": "Starts Roam. Should only be called once. Calling multiple times will result in a promise rejection.\n\nOptional config argument provides lifecycle hooks.\n\n```lua\nRoam.start({\n\tGlobalPreInit = function()\n\t\tprint(\"=== Initialization Phase Starting ===\")\n\tend,\n\tPreInit = function(service)\n\t\tprint(\"Initializing:\", Roam.getServiceName(service))\n\tend,\n\tPostInit = function(service)\n\t\tprint(\"✓ Initialized:\", Roam.getServiceName(service))\n\tend,\n\tGlobalPostInit = function()\n\t\tprint(\"=== All Services Initialized ===\")\n\tend,\n})\n:andThenCall(print, \"Roam started!\")\n:catch(warn)\n```\n\n:::caution\nBe sure that all services have been created _before_\ncalling `Start`. Services cannot be added later.\n:::",
            "params": [
                {
                    "name": "config",
                    "desc": "",
                    "lua_type": "StartConfig?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise\n"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 538,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "onStart",
            "desc": "Returns a promise that is resolved once Roam has started. This is useful\nfor any code that needs to tie into Roam services but is not the script\nthat called `start`.\n```lua\nRoam.onStart():andThen(function()\n\tlocal MyService = require(ReplicatedStorage.MyService)\n\tMyService:DoSomething()\nend):catch(warn)\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Promise"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 725,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "isReady",
            "desc": "Returns whether or not Roam has been successfully started and is ready for external access.",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean\n"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 736,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "requireModules",
            "desc": "Requires all the modules that are children of the given parent. This is an easy\nway to quickly load all services that might be in a folder. Takes an optional predicate\nfunction to filter which modules are loaded. Services collected this way must not yield.\n- `DeepSearch` -> whether it checks descendants or just children\n- `AllowYieldingRequires` -> whether to allow required modules to yield (default: false)\n- `RequirePredicate` -> a predicate function that determines whether a module should be required\n- `IgnoreDescendantsPredicate` -> A Predicate for whether the Descendants of an instance should be Searched (Only matters if DeepSearch is true)\n- `StopOnFailedRequire` -> whether to stop requiring modules if one fails to require (default: false). Useful for debugging, helps to clear excessive noise from the output.\n\n```lua\nlocal pred = function(obj: ModuleScript): boolean\n\treturn obj.Name:match(\"Service$\") ~= nil\nend\n\nRoam.requireModules(ReplicatedStorage.Shared, {\n\tDeepSearch = true,\n\tRequirePredicate = pred,\n\tIgnoreDescendantsPredicate = function(obj: Instance): boolean\n\t\treturn obj.Name == \"Ignore\"\n\tend,\n})\n```",
            "params": [
                {
                    "name": "parents",
                    "desc": "",
                    "lua_type": "Instance | { Instance }"
                },
                {
                    "name": "config",
                    "desc": "",
                    "lua_type": "{\n\t\tDeepSearch: boolean?,\n\t\tAllowYieldingRequires: boolean?,\n\t\tStopOnFailedRequire: boolean?,\n\t\tRequirePredicate: ((obj: ModuleScript) -> boolean)?,\n\t\tIgnoreDescendantsPredicate: ((obj: Instance) -> boolean)?,\n\t}?\n"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{\n\tSuccess: boolean,\n\tModuleContent: { [ModuleScript]: any },\n\tFailedModuleRequires: { ModuleScript },\n}\n"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 764,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "printDependencyGraph",
            "desc": "Prints the dependency graph of all registered services to the output.\nShows the full recursive dependency tree for each service with tree symbols.",
            "params": [],
            "returns": [],
            "function_type": "static",
            "tags": [
                "debug"
            ],
            "private": true,
            "source": {
                "line": 861,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "getServiceState",
            "desc": "Returns the current lifecycle state of a service.\n\n\n```lua\nlocal state = Roam.getServiceState(\"MyService\")\nif state == \"STARTED\" then\n\tprint(\"MyService is fully running!\")\nend\n```",
            "params": [
                {
                    "name": "serviceName",
                    "desc": "Either the name of the service or the service table itself",
                    "lua_type": "string | Service"
                }
            ],
            "returns": [
                {
                    "desc": "The current state of the service, or nil if not found",
                    "lua_type": "ServiceState?"
                }
            ],
            "function_type": "static",
            "tags": [
                "debug"
            ],
            "private": true,
            "source": {
                "line": 965,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "getServiceName",
            "desc": "Fetches the name of a registered Service.",
            "params": [
                {
                    "name": "service",
                    "desc": "",
                    "lua_type": "Service"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "string\n"
                }
            ],
            "function_type": "static",
            "tags": [
                "debug"
            ],
            "private": true,
            "source": {
                "line": 994,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "createService",
            "desc": "Creates a Service/Table with Roam to be Initialized and Started when Roam starts.\nCannot be called after Roam has been started.\n\nThis is an alternative method to setting up services over using `registerService`.\n\n```lua\nlocal Roam = require(ReplicatedStorage.Roam)\n\nlocal MyService = Roam.createService { Name = \"MyService\" }\n\nfunction MyService:DoSomething()\n\tprint(\"yeee haw!\")\nend\n\n-- Default StartMethodName is \"RoamStart\" (Can be overriden in service creation config)\nfunction MyService:RoamStart()\n\tprint(\"MyService started!\")\n\tself:DoSomething()\nend\n\n-- Default InitMethodName is \"RoamInit\" (Can be overriden in service creation config)\nfunction MyService:RoamInit()\n\tprint(\"MyService initialized!\")\nend\n\nreturn MyService\n```",
            "params": [
                {
                    "name": "serviceDef",
                    "desc": "",
                    "lua_type": "ServiceConfig"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Service\n"
                }
            ],
            "function_type": "static",
            "deprecated": {
                "version": "0.1.5",
                "desc": null
            },
            "private": true,
            "source": {
                "line": 1037,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "getService",
            "desc": "Fetches a registered Service by name.\nCannot be called until Roam has been started.",
            "params": [
                {
                    "name": "serviceName",
                    "desc": "",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Service\n"
                }
            ],
            "function_type": "static",
            "deprecated": {
                "version": "0.1.5",
                "desc": null
            },
            "private": true,
            "source": {
                "line": 1048,
                "path": "lib/roam/src/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "Services",
            "desc": "A table of Services. Only properly accessible after Roam has been started.",
            "lua_type": "{[string]: Service}",
            "deprecated": {
                "version": "0.1.6",
                "desc": null
            },
            "private": true,
            "source": {
                "line": 432,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "Debug",
            "desc": "Whether or not to print debug messages. Default is false.",
            "lua_type": "boolean",
            "tags": [
                "debug"
            ],
            "private": true,
            "source": {
                "line": 443,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "DEFAULT_SRC_NAME",
            "desc": "The default name of the source folder where your modules are located. Default is \"src\".\nThis is only used by the generic Bootstrappers provided with Roam.",
            "lua_type": "string",
            "private": true,
            "source": {
                "line": 452,
                "path": "lib/roam/src/init.luau"
            }
        },
        {
            "name": "Bootstrappers",
            "desc": "A table of generic bootstrappers for Roam that you can use to quickly setup new projects.\n```lua\nRoam.Bootstrappers.Server(script)\n:andThenCall(print, \"Roam Server Bootstrapped!\")\n```",
            "lua_type": "{Server: (script: Script) -> Promise, Client: (script: Script) -> Promise}",
            "source": {
                "line": 463,
                "path": "lib/roam/src/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "Service",
            "desc": "A service is a table that can be registered with Roam.",
            "lua_type": "table",
            "source": {
                "line": 13,
                "path": "lib/roam/src/types.luau"
            }
        },
        {
            "name": "ServiceConfig",
            "desc": "```lua\nlocal myOtherService = require(ReplicatedStorage.MyOtherService)\n\n-------------------------------------------------\n\nlocal MyService = {}\n\nfunction MyService:CustomStartMethod()\n\tprint(\"MyService started!\")\nend\n\n-------------------------------------------------\n\nRoam.registerService(MyService, {\n\tName = \"MyService\",\n\tRequiredServices = {myOtherService},\n\tStartMethodName = \"CustomStartMethod\",\n})\n\nreturn MyService\n```\n\n:::caution Deferring RequiredServices\nDo NOT add services to the RequiredServices after you have created or registered the service. This will cause undefined behavior.\n:::",
            "fields": [
                {
                    "name": "Name",
                    "lua_type": "string",
                    "desc": "Name of the Service. Must be unique. Used when accessing via .getService"
                },
                {
                    "name": "RequiredServices",
                    "lua_type": "{Service}?",
                    "desc": "The Services that this Service depends on. Roam will ensure that these Services are initialized before this Service."
                },
                {
                    "name": "StartMethodName",
                    "lua_type": "string?",
                    "desc": "Overrides default StartMethodName of \"RoamStart\""
                },
                {
                    "name": "InitMethodName",
                    "lua_type": "string?",
                    "desc": "Overrides default InitMethodName of \"RoamInit\""
                }
            ],
            "source": {
                "line": 49,
                "path": "lib/roam/src/types.luau"
            }
        },
        {
            "name": "ServiceState",
            "desc": "Represents the current lifecycle state of a service:\n- `REGISTERED` - Service has been registered but not yet initialized\n- `AWAITING_DEPENDENCIES` - Service is waiting for its RequiredServices to finish initializing\n- `PRE_INIT` - Calling PreInit with service\n- `ROAM_INIT` - Calling Service:RoamInit method\n- `POST_INIT` - Calling PostInit with service\n- `INITIALIZED` - Service has completed all initialization steps\n- `PRE_START` - Calling PreStart with service\n- `ROAM_START` - Calling Service:RoamStart method\n- `POST_START` - Calling PostStart with service\n- `STARTED` - Service is actively running and has completed its startup routine\n- `FAILED` - Service initialization failed",
            "lua_type": "\"REGISTERED\" | \"AWAITING_DEPENDENCIES\" | \"PRE_INIT\" | \"ROAM_INIT\" | \"POST_INIT\" | \"INITIALIZED\" | \"PRE_START\" | \"ROAM_START\" | \"POST_START\" | \"STARTED\" | \"FAILED\"",
            "tags": [
                "debug"
            ],
            "private": true,
            "source": {
                "line": 77,
                "path": "lib/roam/src/types.luau"
            }
        },
        {
            "name": "StartConfig",
            "desc": "Yielding in `Init` lifecycle hooks will prevent Roam from progressing to the next step.\n\n`Start` lifecycle hooks are fully asynchronous and do not block progression at any point.",
            "fields": [
                {
                    "name": "GlobalPreInit",
                    "lua_type": "(() -> ())?",
                    "desc": "Called once before ANY service initializes"
                },
                {
                    "name": "PreInit",
                    "lua_type": "((service: Service) -> ())?",
                    "desc": "Called before each service's RoamInit"
                },
                {
                    "name": "PostInit",
                    "lua_type": "((service: Service) -> ())?",
                    "desc": "Called after each service's RoamInit"
                },
                {
                    "name": "GlobalPostInit",
                    "lua_type": "(() -> ())?",
                    "desc": "Called once after ALL services finish initializing"
                },
                {
                    "name": "GlobalPreStart",
                    "lua_type": "(() -> ())?",
                    "desc": "Called once before ANY service starts. (Async)"
                },
                {
                    "name": "PreStart",
                    "lua_type": "((service: Service) -> ())?",
                    "desc": "Called before each service's RoamStart (Async)"
                },
                {
                    "name": "PostStart",
                    "lua_type": "((service: Service) -> ())?",
                    "desc": "Called after each service's RoamStart (Async)"
                },
                {
                    "name": "GlobalPostStart",
                    "lua_type": "(() -> ())?",
                    "desc": "Called once after ALL services start (Async)"
                }
            ],
            "source": {
                "line": 106,
                "path": "lib/roam/src/types.luau"
            }
        }
    ],
    "name": "Roam",
    "desc": "Roam is a systems bootstrapping tool for Roblox luau projects, designed to make it easy to\ninitialize and start services in a topologically sorted manner without the need to\nmanually order and start services.\n\nRoam follows a design pattern similar to [Knit](https://sleitnick.github.io/Knit/), but is more lightweight. \nIt removes all networking and replication functionality, and instead focuses on providing a simple methodology \nto easily initialize and start Services given to it.\n\nRoam is RunContext agnostic, meaning it can be used on both the server and client in the same manner.\nIt makes no distinction between the two, and instead focuses on providing a simple\ninterface for initializing and starting services. This means you could create a service and register it on \nboth the server and client, and it will be initialized and started on both ends. \n\n**[EXAMPLE SERVICE]**\n```lua \n-- MyService.lua\nlocal MyService = {}\n\nfunction MyService:RoamInit()\n\tprint(\"MyService initialized!\")\nend\n\nfunction MyService:RoamStart()\n\tprint(\"MyService started!\")\nend\n\n-- Register the service table with Roam\nlocal Roam = require(ReplicatedStorage.Roam)\nRoam.registerService(MyService, \"MyService\")\n\nreturn MyService\n```\n\n**[EXAMPLE STARTUP]**\n```lua \n-- ServerBootstrapper.server.lua\nlocal Roam = require(ReplicatedStorage.Roam)\n\n-- Require your services. (Tip: Roam.requireModules can help abstract this process!)\nrequire(ReplicatedStorage.MyService)\n\n-- Start Roam\nRoam.start()\n:andThenCall(print, \"Roam started!\")\n:catch(warn)\n```\n\n**[CONTRACTS]**\n- Services must be created/registered before Roam is started.\n- Services must be created/registered with a unique name.\n- Services with `RoamInit` and `RoamStart` methods will have those methods\n  called when Roam is started at the appropriate time. (Names are configurable)\n- `RequiredServices` boot in proper topological order if specified in the ServiceConfig.\n- Roam functions the same regardless of RunContext (Server/Client).\n\n:::info Setting up Services\nServices can be set up in a variety of ways. The most common way is to create a ModuleScript\nthat returns a table with the methods you want to define, and then register it with Roam just prior\nto the final module's return.\n\nSee [Roam.registerService](Roam#registerService) for more information on setting up a new service.\n:::\n\n:::tip Networking\nRoam does not inherently have networking functionality. However, it can easily be added through the use of NetWire's \n**[.setupServiceNetworking](https://raild3x.github.io/ModulesOnRails/api/ServerNetWire/#setupServiceNetworking)** function.\n:::\n\n----\nLIFECYCLE\n----\nFor those interested in the full execution order roam follows for booting services, see the diagrams below:\n\n**Initialization Phase (Synchronous):**\n- `GlobalPreInit` - Called once before ANY service initializes (global setup)\n- For each service (in dependency order):\n\t- Await dependencies to finish initializing\n\t- `PreInit(service)` - Called before this service's RoamInit\n\t- Service's `RoamInit()` method executes\n\t- `PostInit(service)` - Called after this service's RoamInit\n- `GlobalPostInit` - Called once after ALL services finish initializing\n\n**Start Phase (Fully Async - Does Not Block):**\n- `GlobalPreStart` - Called once before ANY service starts.\n- For each service (in dependency order):\n\t- `PreStart(service)` - Called before this service's RoamStart\n\t- Service's `RoamStart()` method executes\n\t- `PostStart(service)` - Called after this service's RoamStart\n- `GlobalPostStart` - Called once after ALL services start\n\n\n**Key Concepts:**\n- **Parallel Initialization**: Services without dependencies initialize concurrently via coroutines\n- **Dependency Blocking**: Services wait for their dependencies' PostInit before starting their PreInit\n- **Synchronous Init Phase**: The Initialization Phase completes fully before Start Phase begins\n- **Async Start Phase**: The Start Phase spawns services lifecycle methods asynchronously\n\n![Advanced Roam Lifecycle Diagram](../roam/RoamLifecycleAdvanced.png)",
    "source": {
        "line": 107,
        "path": "lib/roam/src/init.luau"
    }
}