Services
Services are the primary unit of business logic in a Shiva module. A service is a Lua table (class) registered with the container.
What Is a Service?
A service:
- Holds state (if needed) as instance variables
- Implements a contract interface
- Has no global state — everything is injected
- Is testable in isolation
Creating a Service
bash
shiva make service my-module MyServiceThis creates server/services/MyService.lua:
lua
local MyService = {}
MyService.__index = MyService
--- Create a new MyService instance
---@param deps table
function MyService.new(deps)
return setmetatable({
_db = deps.db,
_player = deps.player,
}, MyService)
end
--- Example method
---@param playerId number
---@return table|nil
function MyService:getDataForPlayer(playerId)
return self._db:first('SELECT * FROM my_table WHERE player_id = ?', playerId)
end
return MyServiceRegistering a Service
lua
-- server/init.lua
local M = {}
function M.init(container)
local MyService = require('server.services.MyService')
container:bind('IMyContract', function()
return MyService.new({
db = container:make('IDatabase'),
player = container:make('IPlayer'),
})
end)
end
return MService Rules
- One responsibility — a service should do one thing
- No
requireof other services — take them as constructor arguments - Return structured errors — don't
error()unless something is truly unexpected - No side effects in constructors — construction should be cheap and deterministic
Testing a Service
Since services take their dependencies as arguments, they're easy to test:
lua
-- spec/MyService_spec.lua
local MyService = require('server.services.MyService')
describe('MyService', function()
it('returns nil for unknown player', function()
local service = MyService.new({
db = MockDB.new({ returns = nil }),
player = MockPlayer.new(),
})
assert.is_nil(service:getDataForPlayer(999))
end)
end)