I’m currently implementing SSA (Server-Sided Architecture) to handle the startup sequence, I’ve created a main ServerScript that requires a module (let’s call it MainModule) which in turn requires other modules (like NetworkModule, GameManager, etc.). These modules may also require additional modules so basically a nested structure of module requires.
This is how my game initializes on both the server and client side.
However I’ve heard that its best to use “Module loaders”, which from my understanding is a script which loads services and then caches the result.
My main question is that with the module loader, do I require all server sided modules or only a few modules which in turn require some others?
Here’s an architecture / module loader that would handle your module startups. It’s the Vesteria’s github / repo, you can review their startup sequence on both server-side and client-side.
With a module loader, you can require modules in shared folders, like ReplicatedStorage. For example, if you have a module that acts like a middleware or a module that’s used by both server and client, you can write the directory into your module loader. Just be aware that any module in shared folders can be viewed from the client.
Ultimately, you should have all of your sensitive code in ServerScriptService, your utilities / libraries / helper functions in ReplicatedStorage, and your client code in your PlayerStarterScripts or in shared folders; it depends on your situation, but it’s conventional to have them in those directories.
But to answer your question: You can do either, as long as your code works with the architecture. You should have an initializing function for server scripts that do need to be initialized, so essentially: you should only require what needs to be required, and what needs to be initialized, and avoid requiring anything that doesn’t need to be required on startup.
These modules that do need to be required on startup, I’m guessing they should be called services? so like DataService, PlayerService. I’m trying to get my naming conventions correct aswell.
Your naming convention is up to you, but from working with big studios, the naming convention works best when it’s simple and straight-forward.
So, if you have a module that handles player data on join, it can be called “PlayerDataSystem” or “PlayerDataService”. It’s really up to you and what you prefer.
However, redundancy can be an issue when you have folders with called “Server” or “Services” or “Systems”, right? Consider the names of your directories:
Is it redundant to name my script “PlayerDataService” if it’s in a folder called “Services”?
Does my naming match the context of the module?
Is the naming consistent across all other modules?
To help a bit on the context of the naming though, I prefer to call “Services” for modules that are utilized on top of systems. For example, if I have multiple systems that handle purchases, I’d want to have a service called “TransactionService” to handle payments. So, it’s easier to understand that Services are used across multiple systems.
To put it simply, you can put everything in one folder, or have subfolders in a folder that separates systems and services. What really matters is if you have an initializing function or not, because that determines your startup sequence. If you’re going to initialize all of your services and systems, then it’s important to prevent the module loader from yielding. You can include task.spawn for requiring and initializing, or you can use pcall, there’s a lot of solutions to prevent yielding.
That works perfectly fine. There’s a lot of different ways to structure your architecture, but yeah this is a conventional way to do it, and your naming is up to you.
This is the way I’ve got my server/client initialization setup (Courtesy of my friend)
I’ve got all my server modules inside the Services folder and what it does is requires all the scripts puts them into a table then it sorts the modules by priority (lower = higher priority) then runs an Init function which basically just does setup if the script needs and after it’ll run the Start function which does the actual task knowing all the other modules have already got what they need.
local Services = {}
for _, Service in script.Services:GetChildren() do
table.insert(Services, require(Service))
end
table.sort(Services, function(a, b)
return (a.Priority or 0) > (b.Priority or 0)
end)
for _, Service in Services do
if not Service.Init then continue end
Service:Init()
end
for _, Service in Services do
if not Service.Start then continue end
Service:Start()
end
This would be the first module to load, even though it doesn’t have any function to run-by if they did exist this would be the first module to have it executed on.
local module = {}
module.Priority = -1
return module