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.