Data Layer

Last Updated: May 19, 2025

This document provides an overview of the Data Layer in the Typus Development Framework, focusing on the Domain-Specific Language (DSL) system that enables consistent data access across frontend and backend.

Overview

The Typus Development Framework implements a unified data access layer through a Domain-Specific Language (DSL) system. This approach allows developers to use the same syntax and patterns for data operations regardless of where the code is executed - backend, frontend, or dispatcher.

The key feature of this system is the ability to write queries like user.findMany() with identical syntax across the entire application stack, significantly reducing context switching and improving developer productivity.

Architecture

The DSL system consists of several interconnected components:

Frontend Application

Frontend DSL Client

Backend Services

Backend DSL Service

Task Dispatcher

Dispatcher DSL Client

API Layer

Prisma ORM

Database

Shared DSL Registry

Key Components

Shared Registry

The shared registry is the central component that maintains model definitions accessible to all parts of the application:

  • Model Definitions: Describes data structures, fields, and relationships
  • Type Information: Provides TypeScript interfaces for type safety
  • UI Metadata: Contains display information for frontend rendering

Backend DSL Service

The backend DSL service processes data operations:

  • Operation Handling: Processes create, read, update, and delete operations
  • Access Control: Enforces permissions based on user roles
  • Hooks System: Provides before/after hooks for data manipulation
  • Prisma Integration: Uses Prisma as the ORM layer to interact with the database

Frontend DSL Client

The frontend DSL client provides a type-safe API for data access:

  • Type-Safe API: Offers strongly-typed methods for data operations
  • Proxy-Based Access: Dynamically creates model clients on demand
  • Consistent Interface: Mirrors the backend API for seamless integration

Dispatcher DSL Client

The task dispatcher uses the same DSL system for scheduled and background tasks:

  • Consistent Data Access: Uses the same patterns as frontend and backend
  • Background Processing: Handles data operations in scheduled tasks
  • Event-Driven Operations: Responds to system events with data operations

Data Flow

A typical data operation flows through the system as follows:

  1. Client Request: Frontend or dispatcher makes a DSL request (e.g., user.findMany())
  2. API Layer: Request is sent to the backend API
  3. DSL Controller: Backend DSL controller receives and validates the request
  4. DSL Service: Service processes the operation with appropriate hooks
  5. Prisma Layer: Prisma ORM translates the operation to database queries
  6. Database: Database executes the query and returns results
  7. Response Processing: Results flow back through the layers with appropriate transformations

Hooks System

The DSL system includes a powerful hooks mechanism that allows for custom logic at various points in the data lifecycle:

  • Before Hooks: Execute before data operations (validation, transformation)
  • After Hooks: Execute after data operations (enrichment, notifications)
  • Custom Logic: Implement business rules without modifying core data access code

Example of registering a hook:

dslService.registerHook(
  'User',
  'beforeCreate',
  (data) => {
    // Custom logic before user creation
    return data;
  }
);

Prisma Integration

Currently, Prisma ORM serves as the data access layer between the DSL system and the database:

  • Schema Definition: Prisma schema defines the database structure
  • Query Building: Prisma translates DSL operations into optimized SQL
  • Type Safety: Prisma provides TypeScript types that integrate with the DSL system

Usage Examples

Backend Usage

// In a backend service
const users = await this.dslService.executeOperation(
  'User',
  'read',
  null,
  { role: 'admin' }
);

Frontend Usage

// In a Vue component
const users = await DSL.User.findMany({ role: 'admin' });

Dispatcher Usage

// In a scheduled task
const inactiveUsers = await DSL.User.findMany({ 
  lastLoginAt: { lt: oneMonthAgo } 
});

Benefits

The unified DSL approach provides several key benefits:

  1. Consistent Developer Experience: Same syntax across all application layers
  2. Reduced Context Switching: No need to learn different data access patterns
  3. Type Safety: Full TypeScript support throughout the stack
  4. Centralized Business Logic: Hooks provide a single place for business rules
  5. Simplified Testing: Consistent mocking approach for all data operations

Conclusion

The DSL system in the Typus Development Framework provides a powerful abstraction over data access, enabling developers to work with a consistent API regardless of where the code is executed. By centralizing model definitions and providing a unified interface, the system significantly improves developer productivity and code maintainability.

The current implementation uses Prisma as the ORM layer, but the architecture is designed to be adaptable to other data access technologies in the future.

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" }