Domain Specific Language (DSL) System

Last Updated: May 11, 2025

The DSL system is a core architectural component of the Typus Development Framework that serves as a single source of truth for application components. It defines entities, relationships, and metadata that can be used to generate backend and frontend artifacts automatically.

System Architecture

The DSL system spans across three main components:

Shared DSL
Model Definitions

Backend DSL
API Endpoints

Frontend DSL
Typed Client

Component Relationships

  1. Shared → Backend:

    • Models are imported by the backend
    • Registry is used to access model definitions
    • Types are used for validation
  2. Shared → Frontend:

    • Interfaces are imported by the frontend
    • Types are used for type checking
    • Constants are used for field references
  3. Backend → Frontend:

    • API endpoints are called by the frontend client
    • Data is returned in a consistent format
    • Operations are validated against model definitions

Core Concepts

Types

The DSL system is built on TypeScript types that define the structure of models, fields, and relationships:

export interface DslField {
  name: string;
  type: string;
  required?: boolean;
  unique?: boolean;
  default?: any;
  primaryKey?: boolean;
  autoincrement?: boolean;
  ui?: {
    isSearchable?: boolean;
    [key: string]: any;
  };
  [key: string]: any;
}

export interface DslRelation {
  name: string;
  type: 'hasMany' | 'belongsTo' | 'hasOne' | 'manyToMany';
  target: string;
  foreignKey?: string;
  inverseSide?: string;
  [key: string]: any;
}

export interface DslModel {
  name: string;
  module?: string;
  tableName?: string;
  fields: DslField[];
  relations?: DslRelation[];
  [key: string]: any;
}

Models

Models are defined using the DSL types and represent the entities in the application. For example:

export const UserModel: DslModel = {
  name: 'User',
  module: 'auth',       
  tableName: 'users',
  fields: [
    {
      name: 'id',
      type: 'number',
      required: true,
      unique: true
    },
    {
      name: 'email',
      type: 'string',
      required: true,
      unique: true,
      validation: [
        { type: 'required' },
        { type: 'email' }
      ],
      ui: {
        label: 'Email',
        component: 'dxInput'
      }
    },

  ],
  relations: [
    {
      name: 'roles',
      type: 'manyToMany',
      target: 'Role',
      inverseSide: 'users'
    }
  ]
};

Models can be organized by modules (e.g., 'auth', 'notifications') to match the application's structure.

Registry

The registry is a central access point for all model definitions:


import { registry } from './registry';
import { UserModel, RoleModel } from './models';

registry.registerMany([UserModel, RoleModel]);


const userModel = registry.getModel('User');
const allModels = registry.getAllModels();

Code Generation

The DSL system can generate various artifacts from model definitions:

1. Prisma Schema Generation

DSL Models

DSL Registry

Prisma Schema Generator

Prisma Schema

Database Migration

The system generates Prisma schema files from DSL model definitions:

import { prismaGenerator } from './generators';
import { registerAllModels } from './register-models';

// Register models
registerAllModels();

// Generate Prisma schema
const schema = prismaGenerator.generate();

2. TypeScript Interface Generation

DSL Models

DSL Registry

Interface Generator

TypeScript Interfaces

The system generates TypeScript interfaces from DSL model definitions:

import { interfaceGenerator } from './generators';
import { registerAllModels } from './register-models';

// Register models
registerAllModels();

// Generate TypeScript interfaces
const interfaces = interfaceGenerator.generate();

Database Migration Process

The DSL system includes a robust database migration process:

Changes Detected

No Changes

Approve

Reject

Update DSL Models

Generate Prisma Schema

Check for Changes

Prepare Migration

No Migration Needed

Review Migration SQL

Apply Migration

Modify DSL Models

  1. Define/Update Models: Create or modify DSL models in shared/dsl/models/
  2. Generate Prisma Schema: Run generator to create Prisma schema from DSL models
  3. Check for Changes: Compare DSL models with Prisma schema
  4. Prepare Migration: Create migration files for schema changes
  5. Apply Migration: Execute migration against the database

Auto-Generated vs Manual Models

The system supports both auto-generated and manually created models:

  • Auto-generated models: Created from DSL definitions, saved with auto. prefix
  • Manual models: Created or modified by hand

When generating the final schema:

  1. Manual models take precedence over auto-generated ones
  2. Both are combined into a single schema file
  3. Source information is preserved in comments

Module-Based Organization

Models are organized by modules, which:

  1. Group related models together
  2. Determine table naming with module prefix (e.g., auth.users)
  3. Match the application's logical structure

Benefits

  1. Single Source of Truth: All definitions in one place
  2. Type Safety: TypeScript provides compile-time checks
  3. Reduced Duplication: Generate code instead of writing it
  4. Consistency: Frontend and backend share the same definitions
  5. Centralized Access: Registry provides a single point of access to models
  6. Dependency Management: Registry can handle relationships between models
  7. Flexibility: Support for both auto-generated and manual models
  8. Modularity: Models organized by application modules
  9. Environment-Aware: Configuration via environment variables

WARNING

Failed to fetch dynamically imported module: https://typus.dev/assets/RecursiveNavItem-Cep7andh.js

{ "stack": "AppError: Failed to fetch dynamically imported module: https://typus.dev/assets/RecursiveNavItem-Cep7andh.js\n at https://typus.dev/assets/index-DS79FI73.js:315:420\n at dn (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:13:1385)\n at We (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:13:1455)\n at Ws.t.__weh.t.__weh (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:14:7364)\n at jt (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:13:1866)\n at v (https://typus.dev/assets/vue-vendor-Ct83yDeK.js:14:4019)\n at https://typus.dev/assets/vue-vendor-Ct83yDeK.js:14:4097" }