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.lua

Why 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.AddServices orders initialization once every module is loaded.