-
Notifications
You must be signed in to change notification settings - Fork 20
project structure
This page is a map of the codebase. Use it when you need to find an existing feature or decide where a new file belongs.
- Source tree
- Choosing a location
- App: routing only
- Systems: feature ownership
- Lib: shared project code
- Server: backend code
- Nebula Library
- Import boundaries
src/
├── app/ Next.js routes and framework entrypoints
├── lib/ Reusable Notebook code and primitives
├── systems/ Feature-owned application code
├── server/ Authentication, database, storage, and tRPC
├── nebula-library/ Shared Nebula component-library submodule
├── env.mjs Environment configuration
├── instrumentation.ts Server instrumentation
└── instrumentation-client.ts Browser instrumentation
Start with the question at the top of this diagram and follow the first answer that fits. If none of them fit, talk through ownership before creating a new top-level directory.
flowchart TB
Start{Does Next.js require this path}
Start -->|Yes| App[src/app]
Start -->|No| ServerQuestion{Is it backend infrastructure or a server procedure}
ServerQuestion -->|Yes| Server[src/server]
ServerQuestion -->|No| FeatureQuestion{Does one Notebook feature own it}
FeatureQuestion -->|Yes| Systems[src/systems/feature]
FeatureQuestion -->|No| SharedQuestion{Is it reusable within Notebook}
SharedQuestion -->|Yes| Lib[A named area in src/lib]
SharedQuestion -->|No| NebulaQuestion{Is it shared by Nebula projects}
NebulaQuestion -->|Yes| Nebula[src/nebula-library]
NebulaQuestion -->|No| Discuss[Discuss ownership before adding a directory]
src/app defines the App Router contract. Most pages and API routes are small
entrypoints that hand control to a system or framework adapter.
app/
├── admin/ Moderation entrypoints
├── api/
│ ├── auth/[...all]/route.ts Better Auth adapter
│ ├── autocomplete/route.ts Search handler entrypoint
│ ├── courseNameAutocomplete/ Search handler entrypoint
│ ├── files/[id]/route.ts Note PDF handler entrypoint
│ └── trpc/[trpc]/route.ts tRPC HTTP adapter
├── auth/ Account entrypoint
├── get-started/ Account onboarding entrypoint
├── notes/ Note page entrypoints
├── profile/ Account profile entrypoints
├── report/ Moderation entrypoint
├── settings/ Account entrypoint
├── layout.tsx Root providers and metadata
├── global-error.tsx Next.js global error boundary
├── sitemap.ts Dynamic sitemap
└── manifest and icon files Next.js metadata files
Framework adapters can contain the wiring their framework requires. Feature
behavior does not belong here. A page will usually delegate to a file under
src/systems/<feature>/pages.
Each folder in src/systems owns one major Notebook capability. A system can
use src/lib and src/server, but it cannot import routes from src/app.
systems/account/
├── components/
│ ├── getting-started/ Onboarding wizard
│ ├── profile/ Profile note lists
│ └── settings/ Account settings forms
├── data/utdDegrees.ts Major and minor choices
└── pages/ Auth, onboarding, settings, and profile screens
Start here for sign-in screens, first-time user setup, profile presentation, or account settings.
systems/moderation/
├── components/ Report form and admin header
└── pages/ Report and admin screens
This system owns the user-facing report and admin flows. Their server
procedures stay in src/server/api/routers.
systems/notes/
├── api/file.ts Validated PDF delivery handler
├── components/ Cards, grids, details, ratings, and actions
├── forms/ Create and edit note forms
├── hooks/ Browser upload behavior
├── pages/ Note list, detail, create, and edit screens
└── utils/noteSlug.ts Route parsing and display text
Start here for note cards, PDFs, uploads, editing, saving, ratings, and note page behavior.
systems/search/
├── api/ Autocomplete request handlers
├── components/ Search bar and search-aware header
├── data/ Generated autocomplete datasets
├── pages/HomePage.tsx Search-oriented home screen
├── scripts/ Dataset fetch and generation scripts
└── utils/ Search query and graph logic
The scripts write generated files back into the search system. The shared
section list is the exception. It lives in src/lib/sections because the
server also uses it.
src/lib contains code that is reusable within Notebook. Give shared code a
named home instead of dropping unrelated files into lib/utils.
lib/
├── components/
│ ├── form/ TanStack Form controls and registration
│ └── shared controls Back button, breadcrumbs, confirmation, empty state
├── icons/ Notebook and authentication icons
├── modules/
│ ├── navigation/ Header shell, profile menu, and sidebar
│ ├── registerModal/ Shared sign-in prompt flow
│ └── snackbar/ Application notifications
├── note-files/ Note file identifiers, limits, and URLs
├── schemas/ Account, moderation, and note validation
├── sections/ Shared section types, normalization, and data
├── styles/ Global CSS and shared visual constants
├── trpc/ React and server tRPC adapters and query client
├── types/ Shared transport types
└── utils/ Small feature-independent helpers
Some library files use server types to keep the client and server contract typed. They still cannot reach upward into a feature system or route.
server/
├── api/
│ ├── routers/ File, report, saved-note, section, storage, and user
│ ├── root.ts Application router composition
│ └── trpc.ts Context, serialization, auth, and procedures
├── db/
│ ├── migrations/ Ordered SQL migrations and Drizzle metadata
│ ├── schema/ Database tables, enums, and relations
│ ├── index.ts Database connection
│ └── models.ts Runtime schemas and inferred model types
├── auth.ts Better Auth configuration
└── storage.ts Nebula API storage transport
The backend remains centralized. Moving server code into a feature system would be a broader architecture decision, not a routine file move.
src/nebula-library is a Git submodule shared by Nebula applications. Import
it through @nebula-library/*, not through the Notebook source alias.
A library change has its own branch, commit, and pull request in the Nebula Library repository. After that change is accepted, Notebook can update the recorded submodule commit in a separate, easy-to-review change.
ESLint enforces the main ownership rules:
- Legacy roots such as
@src/components,@src/utils, and@src/datacannot be reintroduced through the project source alias. - Code outside
src/appcannot import route entrypoints through aliases or parent-relative paths. -
src/libandsrc/servercannot import a feature system through aliases or parent-relative paths. - The shared submodule uses the
@nebula-library/*alias.
If a rule feels awkward, check whether the file is in the right place before working around the boundary with a long relative path.