Entity System
The fw24 Entity System provides a powerful, convention-based CRUD system for DynamoDB with TypeScript support. It automatically handles table creation, relationships, validation, and more.
Overview
The entity system consists of three main components:
- Base Entity - Schema and type definitions
- Base Service - CRUD operations and business logic
- CRUD Service - Core database operations
Quick Start
// 1. Define your entity schema
interface UserSchema extends EntitySchema<string, string, string> {
    model: {
        entity: "user",
        version: "1",
        service: "users"
    },
    attributes: {
        id: EntityAttribute<string>,
        email: EntityAttribute<string>,
        name: EntityAttribute<string>
    }
}
// 2. Create your service
class UserService extends BaseEntityService<UserSchema> {
    constructor() {
        super(userSchema, entityConfigurations);
    }
}
// 3. Create your controller
class UserController extends BaseEntityController<UserSchema> {
    constructor() {
        super("user");
    }
}
Features
Automatic CRUD Operations
The system automatically provides these operations for each entity:
- create- Create new entities
- get- Retrieve single entities
- update- Update existing entities
- delete- Remove entities
- list- List/query entities
- upsert- Create or update entities
DynamoDB Integration
- Automatic table creation based on schema
- Efficient querying using access patterns
- Support for complex queries and filters
- Batch operations
- Transaction support
Type Safety
- Full TypeScript support
- Type inference for queries
- Type-safe relationships
- Validation at compile time
Relationships
Support for entity relationships:
interface UserSchema extends EntitySchema<string, string, string> {
    attributes: {
        id: EntityAttribute<string>,
        posts: Relation<PostSchema>({
            type: 'one-to-many',
            entityName: 'post',
            identifiers: {
                source: 'id',
                target: 'userId'
            }
        })
    }
}
Validation
Built-in validation support:
interface UserValidation extends EntityValidations<UserSchema> {
    email: {
        required: true,
        format: 'email'
    },
    name: {
        minLength: 2,
        maxLength: 50
    }
}
Query Building
Flexible query building:
const users = await userService.query({
    filter: {
        age: { gt: 18 },
        status: { eq: 'active' }
    },
    sort: { field: 'createdAt', order: 'desc' },
    limit: 10
});
Event System
Built-in event dispatching:
// Pre/Post operation events
eventDispatcher.on('beforeCreate', async (data) => {
    // Pre-create logic
});
eventDispatcher.on('afterUpdate', async (data) => {
    // Post-update logic
});
Audit Logging
Automatic audit trail:
// Configuration
{
    auditLogger: {
        enabled: true,
        fields: ['email', 'status']
    }
}
// Automatic logging
// [2024-03-14 10:00:00] User updated: id=123, changed={email: 'old@test.com' -> 'new@test.com'}
Authorization
Role-based access control:
// Define permissions
{
    authorizer: {
        create: ['admin'],
        update: ['admin', 'owner'],
        delete: ['admin'],
        read: ['*']
    }
}
Advanced Features
Field Metadata
Define field metadata for UI rendering:
{
    attributes: {
        email: {
            type: 'string',
            label: 'Email Address',
            placeholder: 'Enter your email',
            validation: {
                required: true,
                format: 'email'
            }
        }
    }
}
Relationship Hydration
Control data loading:
// Get user with related posts
const user = await userService.get(id, {
    hydrate: {
        posts: {
            attributes: ['title', 'content']
        }
    }
});
Search and Filtering
Complex search capabilities:
const results = await userService.search({
    keywords: 'john developer',
    filters: {
        status: 'active',
        skills: { contains: 'typescript' }
    },
    sort: { field: 'relevance' }
});
Pagination
Built-in pagination support:
const page = await userService.list({
    page: 2,
    pageSize: 20,
    sort: { field: 'createdAt', order: 'desc' }
});
Best Practices
- 
Schema Design - Use meaningful entity names
- Define proper relationships
- Include field metadata
- Set appropriate validations
 
- 
Service Implementation - Extend base service for custom logic
- Use dependency injection
- Implement proper error handling
- Add business logic validations
 
- 
Controller Design - Keep controllers thin
- Use proper route naming
- Implement proper request validation
- Handle errors gracefully
 
Examples
Basic Entity
// Schema
interface ProductSchema extends EntitySchema<string, string, string> {
    model: {
        entity: "product",
        version: "1"
    },
    attributes: {
        id: EntityAttribute<string>,
        name: EntityAttribute<string>,
        price: EntityAttribute<number>,
        category: EntityAttribute<string>
    }
}
// Service
class ProductService extends BaseEntityService<ProductSchema> {
    async validatePrice(price: number) {
        if (price <= 0) {
            throw new Error('Price must be positive');
        }
    }
    async create(data: CreateEntityItemTypeFromSchema<ProductSchema>) {
        await this.validatePrice(data.price);
        return super.create(data);
    }
}
// Controller
class ProductController extends BaseEntityController<ProductSchema> {
    constructor() {
        super("product");
    }
    @Post('/products/bulk')
    async bulkCreate(req: Request, res: Response) {
        const products = req.body;
        const results = await this.getEntityService().bulkCreate(products);
        return res.json(results);
    }
}
Complex Relationships
// Order system example
interface OrderSchema extends EntitySchema<string, string, string> {
    model: {
        entity: "order"
    },
    attributes: {
        id: EntityAttribute<string>,
        customer: Relation<CustomerSchema>({
            type: 'many-to-one',
            entityName: 'customer'
        }),
        items: Relation<OrderItemSchema>({
            type: 'one-to-many',
            entityName: 'orderItem'
        }),
        status: EntityAttribute<string>,
        total: EntityAttribute<number>
    }
}
Troubleshooting
Common issues and solutions:
- 
Type Errors // Problem: Type 'string' is not assignable to type 'number'
 // Solution: Check schema definitions and use correct types
- 
Query Performance // Problem: Slow queries
 // Solution: Use proper indexes and access patterns
- 
Relationship Issues // Problem: Circular dependencies
 // Solution: Use lazy loading or restructure relationships
API Reference
See the detailed API documentation for:
- BaseEntityService
- BaseEntityController
- EntitySchema
- Query Types
- Validation Types
- Event Types
Contributing
Guidelines for contributing to the entity system:
- Follow TypeScript best practices
- Add proper documentation
- Include tests
- Follow the existing code style