Hi, I’m developing a game, but I’m constantly wondering about the structure of my code. I’m writing all the code in module scripts, but the problem is how to structure it on the server and the client. For example, I want to create notification system. To do this, I’ll write two module scripts: a controller on the client and a handler on the server:
-- On the client
-- ReplicatedStorage/Controllers/NotificationController
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local NotificationController = {}
NotificationController.__index = NotificationController
export type NotificationController = {
ShowNotification: (self: NotificationController, title: string) -> (),
}
local RE = ReplicatedStorage:WaitForChild("NotificationRE") :: RemoteEvent
-- private type
type NotificationControllerImpl = {
shownNotifications: any,
} & NotificationController
-- Just a contructor
function NotificationController.new(): NotificationController
local self: NotificationControllerImpl =
setmetatable({}, NotificationController) :: any
RE.OnClientEvent:Connect(function(...) self:ShowNotification(...) end)
return self
end
-- Method to show the notification
function NotificationController.ShowNotification(
self: NotificationControllerImpl,
title: string
)
-- create notification
end
return NotificationController.new()
-- On the server
-- ServerScriptService/Handlers/NotificationHandler
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local NotificationHandler = {}
NotificationHandler.__index = NotificationHandler
export type NotificationHandler = {
SendNotification: (
self: NotificationHandler,
player: Player,
title: string
) -> (),
}
local RE = Instance.new("RemoteEvent")
RE.Name = "NotificationRE"
RE.Parent = ReplicatedStorage
function NotificationHandler.new(): NotificationHandler
local self: NotificationHandler =
setmetatable({}, NotificationHandler) :: any
return self
end
-- Method to send an event to controller and then show the notification
function NotificationHandler.SendNotification(
self: NotificationHandler,
player: Player,
title: string
)
RE:FireClient(player, title)
end
return NotificationHandler.new()
(These are example classes)
This will allow me to require the controller from the client script or the handler from the server script to display a notification to the player. But I can wrote this differently, through making a single NotificationService module:
-- ReplicatedStorage/Services/NotificationService
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local NotificationService = {}
NotificationService.__index = NotificationService
export type NotificationService = {
ShowNotification: (self: NotificationService, title: string) -> (),
SendNotificationToPlayer: (
self: NotificationService,
player: Player,
title: string
) -> (),
}
local RE
if RunService:IsServer() then
RE = Instance.new("RemoteEvent")
RE.Parent = ReplicatedStorage
RE.Name = "NotificationRE"
else
RE = ReplicatedStorage:WaitForChild("NotificationRE") :: RemoteEvent
end
function NotificationService.new(): NotificationService
local self: NotificationService =
setmetatable({}, NotificationService) :: any
if RunService:IsClient() then
RE.OnClientEvent:Connect(function(...) self:ShowNotification(...) end)
end
return self
end
function NotificationService.SendNotification(
self: NotificationService,
player: Player,
title: string
)
assert(RunService:IsServer(), "This method can only be called from a server")
RE:FireClient(player, title)
end
function NotificationService.ShowNotification(self: NotificationService, title: string)
assert(RunService:IsClient(), "This method can only be called from a client")
-- create notification
end
return NotificationService.new()
Which option is better to use? Not only for this example, but in general