
Security News
rv Is a New Rust-Powered Ruby Version Manager Inspired by Python's uv
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
@synapsestudios/eslint-plugin-data-boundaries
Advanced tools
ESLint plugin to enforce data boundary policies in modular monoliths
ESLint plugin to enforce data boundary policies in modular monoliths using Prisma ORM and slonik.
Rule | Description | Scope |
---|---|---|
no-cross-file-model-references | Prevents Prisma models from referencing models defined in other schema files | Prisma schema files |
no-cross-domain-prisma-access | Prevents modules from accessing Prisma models outside their domain boundaries | TypeScript/JavaScript |
no-cross-schema-slonik-access | Prevents modules from accessing database tables outside their schema boundaries via slonik | TypeScript/JavaScript |
When building modular monoliths, maintaining clear boundaries between domains is crucial for long-term maintainability. ORMs like Prisma and query builders like slonik make it easy to accidentally create tight coupling at the data layer by allowing modules to access data that belongs to other domains.
This ESLint plugin provides three complementary rules to prevent such violations:
npm install --save-dev @synapsestudios/eslint-plugin-data-boundaries
Prerequisites: If you're using TypeScript, you'll also need the TypeScript ESLint parser:
npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin
no-cross-file-model-references
Prevents Prisma models from referencing models defined in other schema files. This rule works with Prisma's multi-file schema feature to ensure each schema file is self-contained within its domain.
Examples of violations:
// membership.prisma
model UserOrganization {
userId String
user User @relation(...) // ❌ Error: User not defined in this file
}
Valid usage:
// auth.prisma
model User {
id String @id
sessions Session[]
}
model Session {
id String @id
userId String
user User @relation(fields: [userId], references: [id]) // ✅ Valid: User is defined in same file
}
no-cross-domain-prisma-access
Prevents TypeScript/JavaScript modules from accessing Prisma models that belong to other domains. This rule analyzes your application code and maps file paths to domains, then ensures modules only access models from their own domain (plus optionally shared models).
Examples of violations:
// In /modules/auth/service.ts
class AuthService {
async getOrganizations() {
return this.prisma.organization.findMany();
// ❌ Error: Module 'auth' cannot access 'Organization' model (belongs to 'organization' domain)
}
}
Valid usage:
// In /modules/auth/service.ts
class AuthService {
async getUser(id: string) {
return this.prisma.user.findUnique({ where: { id } }); // ✅ Valid: User belongs to auth domain
}
}
no-cross-schema-slonik-access
Prevents TypeScript/JavaScript modules from accessing database tables outside their schema boundaries when using slonik. This rule enforces that all table references must be explicitly qualified with the module's schema name and prevents cross-schema access.
Examples of violations:
// In /modules/auth/service.ts
import { sql } from 'slonik';
class AuthService {
async getUser(id: string) {
// ❌ Error: Module 'auth' must use fully qualified table names. Use 'auth.users' instead of 'users'.
return await this.pool.query(sql`
SELECT * FROM users WHERE id = ${id}
`);
}
async getUserOrganizations(userId: string) {
// ❌ Error: Module 'auth' cannot access table 'memberships' in schema 'organization'.
return await this.pool.query(sql`
SELECT * FROM organization.memberships WHERE user_id = ${userId}
`);
}
}
Valid usage:
// In /modules/auth/service.ts
import { sql } from 'slonik';
class AuthService {
async getUser(id: string) {
// ✅ Valid: Fully qualified table name within module's schema
return await this.pool.query(sql`
SELECT * FROM auth.users WHERE id = ${id}
`);
}
async getUserSessions(userId: string) {
// ✅ Valid: Both tables are explicitly qualified with auth schema
return await this.pool.query(sql`
SELECT s.* FROM auth.sessions s
JOIN auth.users u ON s.user_id = u.id
WHERE u.id = ${userId}
`);
}
}
Configuration:
The rule supports the same modulePath
configuration as other rules:
{
'@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
}]
}
Add the plugin to your .eslintrc.js
:
module.exports = {
// Base parser configuration for TypeScript
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2020,
sourceType: 'module',
project: './tsconfig.json',
// DO NOT add .prisma to extraFileExtensions - our custom parser handles these
},
plugins: ['@synapsestudios/data-boundaries'],
overrides: [
// For Prisma schema files - uses our custom parser
{
files: ['**/*.prisma'],
parser: '@synapsestudios/eslint-plugin-data-boundaries/parsers/prisma',
rules: {
'@synapsestudios/data-boundaries/no-cross-file-model-references': 'error'
}
},
// For TypeScript application code
{
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': ['error', {
schemaDir: 'prisma/schema',
modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
}],
'@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
modulePath: '/modules/' // Default - change to '/src/' for NestJS projects
}]
}
}
]
};
For projects using ESLint's flat config (ESM), add to your eslint.config.mjs
:
import eslintPluginDataBoundaries from '@synapsestudios/eslint-plugin-data-boundaries';
import prismaParser from '@synapsestudios/eslint-plugin-data-boundaries/parsers/prisma';
export default [
// 1. Global ignores first
{
ignores: ['eslint.config.mjs', '**/*.prisma']
},
// 2. Prisma config - isolated and first
{
files: ['**/*.prisma'],
ignores: [], // Override global ignore
languageOptions: {
parser: prismaParser
},
plugins: {
'@synapsestudios/data-boundaries': eslintPluginDataBoundaries
},
rules: {
'@synapsestudios/data-boundaries/no-cross-file-model-references': 'error',
},
},
// 3. Your existing TypeScript config here...
// 4. TypeScript files rule config
{
files: ['**/*.ts', '**/*.tsx'],
plugins: {
'@synapsestudios/data-boundaries': eslintPluginDataBoundaries
},
rules: {
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': [
'error',
{
schemaDir: 'prisma/schema',
modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
}
],
'@synapsestudios/data-boundaries/no-cross-schema-slonik-access': [
'error',
{
modulePath: '/src/' // Use '/src/' for NestJS, '/modules/' for other structures
}
],
},
},
];
⚠️ Flat Config Important Notes:
.prisma
then override in Prisma-specific config⚠️ Important Configuration Note:
Do NOT add .prisma
to extraFileExtensions
in your main parser options. The plugin includes a custom parser specifically for .prisma
files that handles Prisma schema syntax correctly. Adding .prisma
to extraFileExtensions
will cause the TypeScript parser to try parsing Prisma files, which will fail.
module.exports = {
extends: ['plugin:@synapsestudios/data-boundaries/recommended']
};
no-cross-domain-prisma-access
schemaDir
(string): Directory containing Prisma schema files, relative to project root. Default: 'prisma/schema'
modulePath
(string): Path pattern to match module directories. Default: '/modules/'
. Use '/src/'
for NestJS projects or other domain-based structures.{
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': ['error', {
schemaDir: 'database/schemas',
modulePath: '/src/' // For NestJS-style projects
}]
}
no-cross-schema-slonik-access
modulePath
(string): Path pattern to match module directories. Default: '/modules/'
. Use '/src/'
for NestJS projects or other domain-based structures.{
'@synapsestudios/data-boundaries/no-cross-schema-slonik-access': ['error', {
modulePath: '/src/' // For NestJS-style projects
}]
}
This plugin supports multiple project structures:
src/
modules/
auth/ # auth domain
service.ts
controller.ts
organization/ # organization domain
service.ts
controller.ts
user-profile/ # user-profile domain
service.ts
src/
auth/ # auth domain
auth.service.ts
auth.controller.ts
organization/ # organization domain
organization.service.ts
organization.controller.ts
user-profile/ # user-profile domain
user-profile.service.ts
Note: For NestJS projects, set modulePath: '/src/'
in your rule configuration.
prisma/
schema/
auth.prisma # Contains User, Session models
organization.prisma # Contains Organization, Membership models
main.prisma # Contains shared models (AuditLog, Setting)
The plugin automatically maps:
/modules/auth/
→ auth
domainauth.prisma
→ auth
domainmain.prisma
and schema.prisma
→ shared
domainPerfect for applications transitioning from monolith to microservices, ensuring clean domain boundaries while maintaining a single codebase.
Enforces DDD principles at the data layer, preventing cross-domain dependencies that can lead to tight coupling.
Helps large teams maintain clear ownership of domains and prevents accidental coupling between team-owned modules.
Particularly valuable when using AI coding tools, which can easily introduce unintended cross-domain dependencies.
The plugin provides clear, actionable error messages:
Module 'auth' cannot access 'Organization' model (belongs to 'organization' domain).
Consider using a shared service or moving the logic to the appropriate domain.
Model field 'user' references 'User' which is not defined in this file.
Cross-file model references are not allowed.
Module 'auth' must use fully qualified table names. Use 'auth.users' instead of 'users'.
Module 'auth' cannot access table 'memberships' in schema 'organization'.
SQL queries should only access tables within the module's own schema ('auth').
no-cross-file-model-references
rule to prevent new violations in schema filesno-cross-domain-prisma-access
to prevent cross-domain access in application codeno-cross-schema-slonik-access
if using slonik to prevent cross-schema SQL queriesError: "extension for the file (.prisma) is non-standard"
This happens when the TypeScript parser tries to parse .prisma
files. Do NOT add .prisma
to extraFileExtensions
. Instead, make sure your configuration uses our custom parser for .prisma
files:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
// DO NOT add extraFileExtensions: ['.prisma'] here
},
plugins: ['@synapsestudios/data-boundaries'],
overrides: [
{
files: ['**/*.prisma'],
parser: '@synapsestudios/eslint-plugin-data-boundaries/parsers/prisma', // This handles .prisma files
rules: {
'@synapsestudios/data-boundaries/no-cross-file-model-references': 'error'
}
}
]
};
Error: "Could not determine schema directory"
Make sure your schemaDir
option points to the correct directory containing your Prisma schema files:
{
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': ['error', {
schemaDir: 'prisma/schema', // Adjust this path as needed
}]
}
Rule not working on certain files
The no-cross-domain-prisma-access
rule only applies to files in directories that match the modulePath
option. By default, this is /modules/
.
For NestJS projects or other domain-based structures, configure modulePath: '/src/'
:
{
'@synapsestudios/data-boundaries/no-cross-domain-prisma-access': ['error', {
schemaDir: 'prisma/schema',
modulePath: '/src/', // ← Add this for NestJS projects
}]
}
Default structure (modulePath: '/modules/'
):
src/
modules/
auth/ # ✅ Will be checked
service.ts
organization/ # ✅ Will be checked
service.ts
utils/ # ❌ Will be ignored
helper.ts
NestJS structure (modulePath: '/src/'
):
src/
auth/ # ✅ Will be checked
auth.service.ts
organization/ # ✅ Will be checked
org.service.ts
utils/ # ❌ Will be ignored
helper.ts
Issues and pull requests are welcome! Please see our Contributing Guide for details.
MIT
Originally developed for internal use at Synapse Studios and opensourced for the community.
FAQs
ESLint plugin to enforce data boundary policies in modular monoliths
The npm package @synapsestudios/eslint-plugin-data-boundaries receives a total of 27 weekly downloads. As such, @synapsestudios/eslint-plugin-data-boundaries popularity was classified as not popular.
We found that @synapsestudios/eslint-plugin-data-boundaries demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 10 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Ruby maintainers from Bundler and rbenv teams are building rv to bring Python uv's speed and unified tooling approach to Ruby development.
Security News
Following last week’s supply chain attack, Nx published findings on the GitHub Actions exploit and moved npm publishing to Trusted Publishers.
Security News
AGENTS.md is a fast-growing open format giving AI coding agents a shared, predictable way to understand project setup, style, and workflows.