Clean Architecture for Roblox Studio
You have built a complete coin game using the framework. Here is a recap of every file and what it does.
ReplicatedStorage/Shared/Framework/Signal.lua
ReplicatedStorage/Shared/Framework/ServiceLocator.lua
ReplicatedStorage/Shared/Framework/Context.lua
ReplicatedStorage/Shared/Framework/Cubit.lua
ReplicatedStorage/Shared/Framework/NetBridge.lua
ReplicatedStorage/Shared/Types/PlayerTypes.lua
Server/Repositories/PlayerRepository.lua
Server/Services/DataService.lua
Server/Bootstrap.server.lua
Client/Cubits/HUDCubit.lua
Client/Views/HUDView.lua
Client/Bootstrap.client.lua
12 files total. 5 are the framework (copy once, never touch). 7 are game-specific.
| File | What it does |
|---|---|
PlayerTypes.lua |
Defines { coins: number } and Default() |
PlayerRepository.lua |
Calls DataStore:GetAsync and SetAsync |
DataService.lua |
Holds the coin cache, exposes AddCoins, fires OnCoinsChanged |
Bootstrap.server.lua |
Wires everything: constructs, registers, connects signals, manages lifecycle |
HUDCubit.lua |
Holds { coins: number } on the client, exposes SetCoins |
HUDView.lua |
Connects CoinsLabel.Text to HUDCubit.OnStateChanged |
Bootstrap.client.lua |
Constructs Cubits, mounts Views, connects NetBridge to Cubits |
CoinsLabel should show 0 immediately on joinIf coins reset to 0 on rejoin, check that BindToClose is present in Bootstrap.server.lua and that you are testing with the multi-player Studio test mode (not single-player), as DataStore requires a published game or Studio with API access enabled.
DataStore calls require API access to be enabled in Studio:
Without this, all GetAsync calls return nil (no error, just no data), and SetAsync silently fails.
Now that the foundation is working, the architecture scales naturally:
Add an inventory system — create InventoryService, add inventory to PlayerTypes, create InventoryCubit and InventoryView.
Add collectible parts — create ItemSpawner in Server/Services/, touch detection delegates to InventoryService:AddItem().
Add a shop — create ShopService on the server, ShopCubit and ShopView on the client, one new NetBridge.OnServer("PurchaseItem", ...) in a ShopController.
Each new feature follows the exact same pattern. The only files that change are Bootstrap (to register the new classes) and the new feature files themselves.
CoinsLabel not found — check that the GUI path is exactly MainGui > HUD > CoinsLabel and that all three instances exist in StarterGui.
Coins always start at 0 — DataStore API access is not enabled in Studio. See above.
Service 'DataService' not found error — ServiceLocator.Register("DataService", dataSvc) is missing or has a typo in Bootstrap.server.lua.
NetBridge: RemoteEvent 'CoinsUpdate' not found error — NetBridge.CreateEvent("CoinsUpdate") was not called in the server Bootstrap, or the client Bootstrap ran before the server Bootstrap created the event. Ensure CreateEvent is called at the top of the server Bootstrap, not inside a callback.