Skip to content

Dependency Architecture Linter

This document explains the dependency validation system that enforces clean architecture rules.

The project uses a custom validation script (scripts/validate-dependencies.ts) that runs as part of the linting process to ensure all packages follow the dependency architecture rules.

The linter enforces these strict dependency rules:

Package TypeCan Depend On
Common pluginsCommon plugins ONLY
Frontend pluginsFrontend plugins OR common plugins
Backend pluginsBackend plugins OR common plugins
Core packagesCommon packages (minimal dependencies)

The script automatically detects package types based on naming conventions:

  • Common: Packages ending with -common or named @checkstack/common
  • Frontend: Packages ending with -frontend, -frontend-plugin, or starting with @checkstack/frontend or @checkstack/ui
  • Backend: Packages ending with -backend, -backend-plugin, or starting with @checkstack/backend
  • Core: The @checkstack/common package
  • External: Non-@checkstack/* packages (always allowed)
  1. Scans all packages in core/* and plugins/* directories
  2. Reads each package.json file
  3. Checks dependencies and peerDependencies
  4. Validates each internal dependency against the architecture rules
  5. Reports violations and exits with error code 1 if any are found

The dependency validation runs automatically with the regular linting:

Terminal window
bun run lint

This command runs:

  1. bun run lint:code - ESLint for code quality
  2. bun run lint:deps - Dependency architecture validation

You can run just the dependency validation:

Terminal window
bun run lint:deps

Or directly:

Terminal window
bun run scripts/validate-dependencies.ts
{
"name": "@checkstack/catalog-common",
"dependencies": {
"@checkstack/backend-api": "workspace:*" // ❌ VIOLATION
}
}

Error:

❌ Dependency Architecture Violations Found:
@checkstack/catalog-common
→ depends on @checkstack/backend-api
→ common packages cannot depend on backend packages

Fix: Depend on @checkstack/common instead:

{
"name": "@checkstack/catalog-common",
"dependencies": {
"@checkstack/common": "workspace:*" // ✅ OK
}
}
{
"name": "@checkstack/catalog-frontend-plugin",
"dependencies": {
"@checkstack/catalog-backend-plugin": "workspace:*" // ❌ VIOLATION
}
}

Error:

❌ Dependency Architecture Violations Found:
@checkstack/catalog-frontend-plugin
→ depends on @checkstack/catalog-backend-plugin
→ frontend packages cannot depend on backend packages

Fix: Depend on common package instead:

{
"name": "@checkstack/catalog-frontend-plugin",
"dependencies": {
"@checkstack/catalog-common": "workspace:*" // ✅ OK
}
}
{
"name": "@checkstack/catalog-common",
"dependencies": {
"@checkstack/common": "workspace:*"
}
}
{
"name": "@checkstack/catalog-frontend-plugin",
"dependencies": {
"@checkstack/frontend-api": "workspace:*",
"@checkstack/catalog-common": "workspace:*",
"@checkstack/ui": "workspace:*"
}
}
{
"name": "@checkstack/catalog-backend-plugin",
"dependencies": {
"@checkstack/backend-api": "workspace:*",
"@checkstack/catalog-common": "workspace:*"
}
}

All packages can depend on external (non-@checkstack/*) packages:

{
"name": "@checkstack/catalog-common",
"dependencies": {
"zod": "^4.2.1",
"react": "^18.2.0"
}
}

The lint check runs in CI/CD pipelines. If dependency violations are detected, the build will fail, preventing broken architectures from being merged.

  1. Enforces Clean Architecture: Prevents runtime-specific code from leaking into shared packages
  2. Prevents Circular Dependencies: Type system issues are caught early
  3. Maintains Separation of Concerns: Frontend, backend, and common code stay properly isolated
  4. Fail Fast: Violations are caught during development, not deployment
  5. Clear Error Messages: Developers immediately know what’s wrong and how to fix it

If you have a package that doesn’t follow naming conventions, it will be treated as “unknown” and won’t be validated. To fix this:

  1. Update the package name to follow conventions
  2. Or update getPackageType() in scripts/validate-dependencies.ts to recognize your package

To add support for new package types (e.g., *-node, *-react):

  1. Add the type to PackageType union in the script
  2. Update getPackageType() to recognize the pattern
  3. Add validation rules in isDependencyAllowed()