YouBothAgent▾
You — Business rules and flows you own. Read these yourself.
Both — Know the idea; your agent follows the details.
Agent — Conventions and references your agent follows. Look up as needed.
Introduction▾
Tutorials▾
System Architecture▾
Module Convention▾
Scalar Convention▾
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
fieldDescription
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:
fieldDescription
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:
fieldDescription
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:
fieldDescription
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:
fieldDescription
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:
fieldDescription
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.
fieldDescription
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.