Database Module in Akan.js

Database modules in Akan.js provide a structured approach to building domain-specific features with seamless integration between server and client. Each module encapsulates all related functionality - from data models to UI components - in a consistent, type-safe structure.
📦Domain Encapsulation
Encapsulate all domain-specific logic in a consistent structure
🔗Full-Stack Integration
Seamless integration between MongoDB, NestJS server, and React client
🔒Type Safety
Ensure type safety across entire stack with auto-generated types
Automated CRUD
Provide automatic CRUD operations through standardized patterns

File Structure

A complete database module follows this structured organization. Think of it like organizing a restaurant kitchen - each station has its specific role, and together they create a seamless dining experience.
Module Directory Structure
constant.ts

Defines data models using the via() function pattern for type-safe schema definitions

document.ts

MongoDB schema with filters, methods, and middleware using from(), by(), into(), beyond()

service.ts

Business logic implementation using serve() with dependency injection

signal.ts

API endpoints using slice() and endpoint() for type-safe client-server communication

store.ts

Client-side state management using store() with automatic slice integration

Creating a Model Schema

Define your data model using the via() function pattern. This approach provides type-safe field definitions with validation, default values, and relationships.
product.constant.ts
Let's understand the key concepts of the model definition:
enumOf()

Creates a type-safe enum class with the given name and values. Use 'as const' to preserve literal types for strict type checking.

via((field) => ({...}))

The field() function defines each property with its type, default value, validation, and other options like .optional() for nullable fields.

Class Hierarchy

Input → Object → Light → Model. Each layer adds specific functionality. Light classes select specific fields for optimized list queries.

Implementing Database Schema

Create MongoDB schema with filters, document methods, and middleware using the functional pattern. This file bridges your constant definitions with the actual database operations.
product.document.ts
Let's understand the document layer functions:
from() - Filter Definition

Defines reusable query patterns with .arg() for required parameters and .opt() for optional ones. The .query() method returns the MongoDB query object.

by() - Document Methods

Creates a document class with instance methods that operate on individual documents. Methods can modify the document and should return 'this' for chaining.

into() - Model Operations

Creates a model class with collection-level operations. Auto-generates query methods from filters (e.g., queryInCategory from inCategory filter).

beyond() - Middleware

Adds schema-level configurations like indexes, pre/post hooks, and virtual fields through the onSchema method.

Business Logic in Services

Services contain the business logic of your application. They orchestrate document operations, handle cross-module dependencies, and implement complex workflows.
product.service.ts
Key aspects of the service layer:
serve()

Creates a service class that automatically inherits CRUD operations from the document model (getProduct, loadProductMany, etc.).

service<T>()

Dependency injection for other services. Declared services are automatically available as this.serviceName in methods.

Method Chaining

Document methods return 'this' allowing chains like product.sell(5).save(). Always call .save() to persist changes.

Defining API Endpoints

Signals define the API layer that connects clients to services. They include slices for data fetching and endpoints for mutations, all with type-safe parameters and guards.
product.signal.ts
Understanding the signal layer components:
slice()

Defines data-fetching endpoints that support pagination, search, and real-time updates. Use .param() for URL params and .search() for query strings.

endpoint() + mutation()

Defines mutation endpoints that modify data. First argument is the return type, then guards, params (.param()), and body data (.body()).

guards

Access control with Admin, User, Every, Public guards. Use .with(Self) to inject the current user context into the exec function.

srv.product.with()

Combines multiple services for cross-module operations. All combined services are available as this.serviceName in exec functions.

Client-Side State Management

The store manages client-side state and provides methods for interacting with the API. It automatically integrates with slices and provides a consistent interface for UI components.
product.store.ts
Understanding the store layer:
store(sig.module, state)

Creates a store class connected to the signal. Auto-generates state and setters for slices (productList, setProduct, productForm, etc.).

fetch

Type-safe API client auto-generated from signals. Methods match signal endpoint names (fetch.sellProduct, fetch.createProduct).

msg

Toast notification helper using dictionary keys. msg.loading(), msg.success(), msg.error() with translation support.

UI Component Organization

UI components are organized by their purpose: Template for forms, Unit for cards, View for details, Zone for layouts, and Util for actions. Each follows a consistent pattern.
Template - Form Components

Form components that use st.use.[model]Form() for state and st.do.set[Field]On[Model] for updates. Wrapped in Layout.Template.

Unit - Card Components

Displays LightModel data in card/list format. Receives the model as a prop and renders summarized information with optional actions.

View - Detail Components

Shows full model details. Receives the complete model (not Light) and displays all relevant information in a structured layout.

Zone - Layout Components

Container components that handle data loading via Load.Units and organize Unit/View components. Often include modals and real-time refresh.

Util - Action Components

Reusable action buttons and utilities (Process, Serve, Cancel). Accept modelId and disabled props, call store methods on click.

Data Flow in Database Modules

Understanding how data flows through the module layers helps you debug issues and design efficient features. Here's the complete flow from user interaction to database:
1
User Interaction
User clicks button in Unit/Template component
2
Store Action
Store method called (st.do.sellProduct)
3
API Call
fetch.sellProduct() sends request to endpoint
4
Signal → Service
Signal exec() calls service method with validated params
5
Document Operation
Document methods update and save to MongoDB
6
UI Update
Store updates state, React re-renders components

Module Best Practices

Following these best practices ensures your modules remain maintainable, performant, and consistent with the Akan.js ecosystem:
1️⃣Naming Conventions
Use PascalCase for classes/components (ProductService, Product.Template.General), camelCase for files (product.service.ts) and methods.
2️⃣Code Organization
Keep business logic in services, use signals only for API definitions. Document methods should be simple state mutations.
3️⃣Performance
Use Light classes for list queries, create proper indexes in middleware, use Promise.all for parallel fetches in loaders.
4️⃣Security
Apply appropriate guards to all endpoints. Use .with(Self) to access current user. Never expose sensitive data in Light classes.
5️⃣UI Components
Separate concerns into Template/Unit/View/Zone/Util. Use props to control behavior (showControls, disabled) for component reuse.
Released under the MIT License
Official Akan.js Consulting onAkansoft
Copyright © 2025 Akan.js. All rights reserved.
System managed bybassman