Skip to content

Repository

Discover how to structure and organize project repositories, including naming conventions, monorepo architecture, package management, and initial setup.

Naming

Repository names should be all lowercase and match the project name exactly, with each project prefixed by the partner name.

zsh
# ✅ Correct
develit-sdk

# ❌ Incorrect
Develit-SDK
DEVELIT-SDK
Develit-sdk
DEVELIT-sdk
develit-SDK

Structure

Each project uses a monorepo architecture, containing all related packages in a single repository. Packages are organized into three main directories based on their architectural role and responsibility.

zsh
project-name
├── apps
   ├── frontend
   ├── gateway
   ├── queue-bus
   └── ...
├── packages
   ├── sdk
   ├── shared
   └── ...
└── services
    ├── auth
    ├── notification
    ├── rbac
    └── ...

Apps

Software that either provides a user interface for end users or coordinates communication and routes requests across services, handling both synchronous and asynchronous operations.

Examples

  • API gateway
  • Web application
  • Queue bus

Packages

Shared code used across apps, services, and for external integrations.

Examples

  • Public SDK
  • Shared types

Services

Standalone software responsible for specific domains, managing their own data and operations.

Examples

  • Auth service
  • Notification service
  • RBAC service

Package Manager

All packages and their dependencies are managed by the Bun package manager.

  • Use bunx instead of npx to run packages without installing
  • Use bun instead of npm, pnpm, or yarn to execute commands

BUN CLI NATIVE COMMANDS

Be careful when executing commands like test or build. These are Bun CLI native commands, which will result in calling Bun's native test runner and bundler.

Calling commands specified in package.json require adding run after bun.

zsh
# ✅ Correct
bun run test # Runs `test` command in `package.json`
bun run build # Runs `build` command in `package.json`

# ❌ Incorrect
bun test # Runs Bun test runner
bun build # Runs Bun bundler

Workspaces

Bun fully supports npm's native workspaces feature. Each previously outlined group is represented as a workspace.

json
{
  "workspaces": [
    "apps/*",
    "packages/*",
    "services/*"
  ]
}

This is later leveraged by Nx, which works seamlessly with Bun workspaces.

Scaffolding

  1. Create a directory called {project-name} that will serve as the repository root.

  2. Create README.md file based on the following pattern:

md
# {project-name}
  1. Create a package.json file based on the following pattern:
json
{
  "name": "{project-name}",
  "version": "0.0.0",
  "author": "Develit.io s.r.o.",
  "private": true,
  "type": "module",
  "workspaces": [
    "apps/*",
    "packages/*",
    "services/*"
  ],
  "scripts": {
    "postinstall": "lefthook install",
    "reset": "rm -rf $(find . -name node_modules -o -name .wrangler -o -name dist -o -name tsbuild -o -name bun.lock)",
    "dev": "wrangler dev --persist-to ./.wrangler/state",
    "deps:graph": "nx graph",
    "types": "nx run-many -t types",
    "typecheck": "nx run-many -t typecheck",
    "lint": "biome check",
    "lint:fix": "biome check --fix",
    "test": "vitest",
    "testcov": "vitest run --coverage",
    "test:unit": "vitest unit fixtures",
    "test:int": "vitest integration",
  },
  "devDependencies": {
    "@biomejs/biome": "^2.3.9",
    "@cloudflare/vitest-pool-workers": "^0.12.3",
    "@commitlint/cli": "^20.2.0",
    "@commitlint/config-conventional": "^20.2.0",
    "@nx/js": "^22.3.3",
    "@vitest/coverage-istanbul": "~3.2.4",
    "alchemy": "^0.83.1",
    "lefthook": "^2.0.15",
    "lint-staged": "^16.2.7",
    "nx": "^22.3.3",
    "typescript": "^5.9.3",
    "vite-tsconfig-paths": "^6.0.4",
    "vitest": "~3.2.4",
    "wrangler": "^4.59.3",
    "zod": "^4.2.1"
  }
}
  1. Create a bunfig.toml file based on the following pattern:
toml
[install]
linker = "isolated"
  1. Create an nx.json file based on the following pattern:
json
{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "tui": {
    "enabled": false
  },
  "plugins": [
    {
      "plugin": "@nx/js/typescript",
      "options": {
        "typecheck": {
          "targetName": "typecheck"
        },
        "build": false
      }
    }
  ]
}
  1. Create a tsconfig.base.json file based on the following pattern:
json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "erasableSyntaxOnly": true,
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "isolatedModules": true,
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "paths": {},
    "resolveJsonModule": true,
    "skipLibCheck": true,
    "strict": true,
    "target": "ESNext",
  }
}
  1. Create a tsconfig.json file based on the following pattern:
json
{
  "extends": "./tsconfig.base.json",
  "files": [],
  "references": []
}
  1. Create an alchemy.run.ts file based on the following pattern:
ts
import type { Project } from '@develit-io/platform-sdk'
import {
  Deployment,
  composeStateStoreName,
  detectEnvironment,
} from '@develit-io/platform-sdk'
import alchemy from 'alchemy'
import { CloudflareStateStore } from 'alchemy/state'

const PROJECT_NAME: Project = '{project-name}'

const app = await alchemy(PROJECT_NAME, {
  stateStore: (scope) => {
    return new CloudflareStateStore(scope, {
      scriptName: composeStateStoreName({ projectName: PROJECT_NAME }),
    })
  },
  stage: detectEnvironment(),
})

const deployment = new Deployment({ project: PROJECT_NAME })

await app.finalize()
  1. Create a .github/dependabot.yaml file based on the following pattern:
yaml
version: 2
updates:
  - package-ecosystem: github-actions
    rebase-strategy: disabled
    schedule:
      interval: weekly
      day: monday
      time: '00:00'
    directory: /
    allow:
      - dependency-type: direct
    commit-message:
      include: scope
      prefix: 'chore'
    labels:
      - Dependencies
  1. Create an biome.jsonc file based on the following pattern:
jsonc
{
  "$schema": "./node_modules/@biomejs/biome/configuration_schema.json",
  "vcs": {
    "enabled": true,
    "clientKind": "git",
    "useIgnoreFile": true
  },
  "files": {
    "ignoreUnknown": true,
    "includes": [
      "**",
      "!**/.wrangler",
      "!**/dist",
      "!**/worker-configuration.d.ts",
      "!**/migrations/**/*",
      "!**/.output",
      "!**/.alchemy/**/*",
      "!**/coverage",
      "!apps/fe-client/**/*",
      "!bun.lock",
      "!**/tsbuild"
    ],
    "maxSize": 2000000
  },
  "formatter": {
    "enabled": true,
    "indentStyle": "space"
  },
  "assist": { "actions": { "source": { "organizeImports": "off" } } },
  "linter": {
    "enabled": true,
    "rules": {
      "recommended": false,
      "correctness": {
        "noUnusedImports": "error",
        "noUnusedVariables": "error"
      },
      "style": {
        "noNonNullAssertion": "off",
        "useImportType": "error"
      },
      "suspicious": {
        "noExplicitAny": "error",
        "noUnsafeDeclarationMerging": "error"
      }
    },
    "includes": ["**", "!apps/fe-client/assets/css/**"]
  },
  "javascript": {
    "formatter": {
      "quoteStyle": "single",
      "semicolons": "asNeeded"
    }
  }
}
  1. Create a .gitignore file based on the following pattern:
zsh
# Operating System
.DS_Store
Thumbs.dll

# Environment
.env
.env*.local
next-env.d.ts

# Dependencies
node_modules

# Wrangler
.wrangler

# Vitest
coverage

# IDE
.idea
.vscode

# Alchemy
.alchemy

# Nuxt
.nuxt

# Next
.next
.open-next

# Build
dist
**/.vitepress/cache

# Nx
.nx

# TypeScript
tsbuild
tsconfig.tsbuildinfo
  1. Create a .env file based on the following pattern:
zsh
ENVIRONMENT=
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_API_TOKEN=
ALCHEMY_STATE_TOKEN=
  1. Create apps, packages, and services directories.

  2. Execute bun install command at the root.