Skip to content

Container (IoC) ​

The container is the dependency injection system at the heart of Shiva. It resolves dependencies, manages lifetimes, and decouples modules from each other.

The Problem It Solves ​

In traditional FiveM resources, everything is global:

lua
-- Old way: direct dependency
local QBCore = exports['qb-core']:GetCoreObject()
QBCore.Functions.GetPlayer(source)

If qb-core renames or changes that function, everything breaks. There's no seam to insert a different implementation.

Shiva's container solves this:

lua
-- Shiva way: depend on a contract
local player = Container:make('IPlayer')
player:get(source)

The container decides which IPlayer implementation to give you. You don't know or care if it's shiva-player or your custom replacement.

Binding ​

A module binds its service to a contract in init():

lua
-- Binding a singleton (one instance, reused everywhere)
container:bind('IPlayer', PlayerService)

-- Binding a factory (new instance every time)
container:bind('IPlayer', PlayerService, { factory = true })

-- Binding a value (a plain table, not instantiated)
container:instance('IConfig', Config)

Resolving ​

Other modules resolve their dependencies in start():

lua
-- Resolve a single binding
local player = container:make('IPlayer')

-- Resolve all bindings for a contract (for plugin-style systems)
local allHooks = container:makeAll('IEconomyHook')

Lifetimes ​

LifetimeBehaviour
singleton (default)One instance created, returned for all make() calls
factoryNew instance created on each make() call
instanceA pre-existing value, never instantiated

Checking Bindings ​

lua
if container:has('IInventory') then
    local inv = container:make('IInventory')
end

Use has() when a dependency is optional.

Error Handling ​

If you call container:make('IPlayer') and nothing is bound to 'IPlayer', Shiva throws a ContractNotBound error with a clear message listing what's missing and which module was expected to provide it.

See Also ​

Released under the MIT License.