Looking for feedback on my framework description/layout/model

Going to bombard you with the text in the modulescript I have describing it:

--[[
	Framework:
	
--]]

--[[
	Organization:
	
	Modules are separated into 3 categories:
		- Client: Stuff only the client uses (game.StarterPlayer.StarterPlayerScripts)
		- Shared: Stuff both the client and server use (game.ReplicatedStorage)
		- Server: Stuff only the server uses (game.ServerStorage)
	
	Each of these 3 categories is further divided into 3 parts:
		- Classes: Abstract(ish) modules that are often reused and should only require other classes, never data nor handlers, furthermore, classes should not have any Init functions
		- Data: Should be purely for data storage (generally constants)
		- Handlers:	Anything else, can interact with the classes and data
		
	Objects are separated into 3 categories:
		- Client: Stuff only the client can use (Client.Objects)
		- Shared: Stuff both the client and server can use (Shared.Objects)
		- Server: Stuff only the server can use (Server.Objects)
	
	Objects should be treated as constants and remain unchanged
	
--]]

--[[
	Module rules:
	
	Classes and handlers should have no children but data can only have modulescripts that hold data (like block data for a blocks data module)
	All objects must be stored in the two object folders
	
	No type of module must ever yield in its basic environment, but can yield in Init and other functions - therefore you must never Init other modules from the base environment
	All modules must return a table but having the Init function is optional
	
--]]

--[[
	Accessing the shared state:
	
	If a module would like to use the shared environment between all other modules, it must add this line in its headers:
		local RB = require(game.RB)
	
	Modules can then interact with each other like so:
		local otherModule = RB.OtherModule
	
	To Init another module, simply do:
		otherModule.Init()
	
	There is no sanity check on init so if your code calls it multiple times, it will be called multiple times.
	
	You can store anything you want in RB
	
	RB.Objects is a union folder of the local (Client/Server) and the shared objects
	
--]]

--[[
	Loading order:
	
	It is required to have a Handler module called "Main" with an Init function - it should start the loading chain
	Modules can (and should, because only Main will be loaded by the framework) load each other
	
	
--]]

--[[
	Circular dependencies:
	
	What are they?
	Circular dependies are when there is a loop in the require graph
	
	What do they do?
	Normally they will permanently hault the thread they are running on, but since this code is cool, the .__index metamethod for RB will throw an error and you can easily figure out where the circular dependency is
	
	How can I avoid them?
	You can avoid circular dependencies by:
		A. Checking your code to make sure no circular dependencies occur
		B. Try putting the circular dependencies in the Init function
		C. Literally just put your entire module in a coroutine.wrap (still return a table though!)
		
	Why do B and C work?
	 - B works because the Init function will be called at a later time after the module has been required and cached
	 - C works because it is running on a different thread
	
--]]

--[[
	A template for how modules should be structured:
	
	--[[
		TemplateModule
		
	--]]
	
	-- Constants
	
	-- efficiency variables
	local RB = require(game.RB)
	
	-- local variables
	
	local Module = {}
	
	function Module.Init()
		
	end
	
	return Module
	
--]]

Some basic questions:

  1. Am I doing something completely wrong/is there a better way to do something?
  2. Is there any situation this would fail/make something very difficult?
  3. Is there ANYTHING you would change about it? (no matter how basic/small)
2 Likes

From the look of it, everyone seems fine about it. :slight_smile:

1 Like

game.RB? Are you doing the thing where you put objects outside of a service?

Just the loader/manager module
Is it a bad idea?

It’s not officially supported so I recommend against it.

I feel like you should avoid circular dependencies all the same. Point B and C work but having a circular dependency might mean that you are not structuring your code hierarchy correctly so I wouldn’t consider them valid solutions.

1 Like

Would it be a good idea to force run (or run and initialize) all of the Classes and add that as a rule that separates classes from modules? (“Classes will automatically be ran/initialized”)