Hello! For my game I have implemented a DataStorage system. How it works is that it has a folder of the User’s data, with ValueObjects to hold the parameters. Here is a visualization of it:

Each ‘attribute’ is stored as its own Instance. However, that is not ideal for what I am planning for: An Inventory System.
Here is the source of my DataManager script:
local RepStore = game:GetService("ReplicatedStorage")
local SvrStore = game:GetService("ServerStorage")
local DatStore = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local PlayerDataFolder = RepStore.PlayerData
local InfoStore = DatStore:GetDataStore("PlayerData")
local ToCall = game.ServerStorage.Events.DataCall
local ServerStore = DatStore:GetDataStore("ServerStore")
local CurFormat = {"Level","Token","Xp","Coin","Playtime","Streak"} -- the default storage variables
local Conversions = {Score="Xp"} -- converts old storage system into new
function UnloadPlayerData(Player)
local IDList = ServerStore:GetAsync("IDlist")
if not IDList then IDList = {} end
if not table.find(IDList,Player.UserId) then
table.insert(IDList,Player.UserId)
ServerStore:SetAsync("IDlist",IDList)
end
local PlayerID = Player.UserId
local suc, err = pcall(function()
local PlayerData = InfoStore:GetAsync(PlayerID)
if not PlayerData then
warn("data empty")
PlayerData = {}
for i, v in pairs(CurFormat) do
PlayerData[v] = 0
end
end
local PlayerFolder = Instance.new("Folder")
PlayerFolder.Name = PlayerID
PlayerFolder.Parent = PlayerDataFolder
for i, v in pairs(CurFormat) do
if not PlayerData[v] then
PlayerData[v] = 0
end
end
for i, v in pairs(PlayerData) do
if Conversions[i] then
local Conv = Conversions[i]
PlayerData[Conv] = v
PlayerData[i] = nil
end
if not table.find(CurFormat,i) then
PlayerData[i] = nil
end
end
for store, data in pairs(PlayerData) do
local IntHolder = Instance.new("IntValue")
IntHolder.Name = store
IntHolder.Value = data
IntHolder.Parent = PlayerFolder
end
end) if not suc then warn(err) end
end
function PackPlayerData(Player)
local PlayerID = Player.UserId
if PlayerID then
local PlayerFolder = PlayerDataFolder:FindFirstChild(PlayerID)
local success, err = pcall(function()
if PlayerFolder then
local PlayerData = {}
for i, v in pairs(PlayerFolder:GetChildren()) do
PlayerData[v.Name] = v.Value
end
InfoStore:SetAsync(PlayerID,PlayerData)
end
end)
if success then
PlayerFolder:Destroy()
else
warn(err)
end
end
end
function TranslateCall(callData)
local suc, err = pcall(function()
for i, v in pairs(callData) do
local PlrId = v.PlrId
local Ttype = v.Type
local value = v.Value
local LPlrFol = PlayerDataFolder[PlrId]
local SVal = LPlrFol[Ttype]
SVal.Value += value
end
end) if not suc then warn(err) end
end
Players.PlayerAdded:Connect(UnloadPlayerData)
Players.PlayerRemoving:Connect(PackPlayerData)
ToCall.Event:Connect(TranslateCall)
I have a few ideas on how this may work. Having an ‘Inventory’ folder under the Player’s Data Folder that stores the ID and amount of the objects that the User has in their inventory. But I am sure there is an easier way to store it.