Conventions and Layout
Semitra is convention-driven. The app tree is not decoration. It is how the framework stays predictable.
Base classes
Section titled “Base classes”All core framework layers use Semitra-prefixed base classes:
SemitraControllerSemitraRecordSemitraResourceSemitraPolicySemitraJobSemitraCacheSemitraStorageSemitraEventsSemitraChannelSemitraMailerSemitraMailbox
Application code usually defines local base classes that extend these once:
ApplicationControllerApplicationRecordApplicationResourceApplicationPolicyApplicationJobApplicationMailerApplicationMailboxApplicationChannel
Conventional app tree
Section titled “Conventional app tree”app/ controllers/ models/ resources/ policies/ jobs/ mailers/ mailboxes/ channels/config/ application.ts routes.tsdb/ migrate/ schema.tssrc/ index.tsWhat each directory means
Section titled “What each directory means”app/controllershandles requests and response orchestration.app/modelscontains records and persistence rules.app/resourcesdefines external response shapes.app/policiescontains authorization and scoping logic.app/jobscontains queue-backed work.app/mailersandapp/mailboxeshandle outbound and inbound mail flows.app/channelshandles realtime subscriptions and broadcasting.config/application.tsconfigures app runtime concerns like tenancy.config/routes.tsdefines the routing DSL.db/migratecontains schema changes.src/index.tsis the Worker entrypoint.
Filesystem-driven behavior
Section titled “Filesystem-driven behavior”Semitra resolves the app root from convention:
- a valid app root contains
config/routes.ts - a valid app root contains
src/index.ts - if you omit
[app-root], the CLI resolves the nearest Semitra app or the single app inside./apps
This is why the conventional layout matters to CLI commands such as dev,
test, and db:migrate.
Runtime-safe rule
Section titled “Runtime-safe rule”Semitra applications run in Cloudflare Workers. Keep that in mind when writing application code:
- prefer Web Standard APIs
- avoid Node-only runtime assumptions in app code
- let subsystem packages own their responsibilities
- let adapters bridge Cloudflare bindings into framework contracts