Project Structure
Owl doesn't enforce a folder structure, Owl.AddServices() and Owl.AddControllers() just recursively require() every ModuleScript under whatever folder you point them at. That said, most projects converge on the same shape and it's the one every code sample in this documentation assumes.
ReplicatedStorage/
└── OwlKnit/
├── Libs/ (Comm, Trove, Signal, Promise, Timer...)
├── Middleware/ (RateLimiter, TypeChecker)
└── Owl < the framework itself
├── OwlServer.lua
├── OwlClient.lua
├── OwlShared.lua
└── OwlComponent.lua
ServerScriptService/
├── Init.server.lua
└── Services/
├── CoinService.lua
├── EnemyService.lua
└── SpawnService.lua
StarterPlayerScripts/
├── Init.client.lua
└── Controllers/
├── CoinController.lua
└── HUDController.luaWhy this shape
ReplicatedStorage/OwlKnit/ holds Owl itself and its dependencies (Libs, Middleware). This is the only folder that needs to be visible to both the server and the client, everything under Services/ and Controllers/ stays where it belongs and is never replicated as source code.
ServerScriptService/Services/ is a flat folder of Service ModuleScripts. Init.server.lua doesn't require() them individually, it just calls Owl.AddServices(script.Parent.Services) once (see Installation) and Owl handles discovery and load order from there based on each Service's Dependencies table (see Dependencies).
StarterPlayerScripts/Controllers/ mirrors the same idea on the client with Owl.AddControllers(script.Parent.Controllers).
Where Components fit
If you're using the Component system, those typically live alongside the Service that owns them rather than in a separate top-level folder for example, Services/EnemyService.lua next to Services/Components/EnemyComponent.lua. There's no hard rule here, keep whatever's easiest to navigate for your team.
Where to go next
- First Project - put this structure to use by building one real Service and Controller.
- Dependencies - how
Owl.AddServicesorders initialization once every module is loaded.