Module Structure
Last Updated: May 19, 2025
This document describes the module structure and patterns used in the Typus Development Framework backend.
Overview
The backend of the Typus Development Framework is organized into modules, each responsible for a specific domain or feature. This modular architecture promotes separation of concerns, code reusability, and maintainability.
Modules are self-contained units that encapsulate related functionality, including API endpoints, business logic, data access, and validation. Each module follows a consistent structure and pattern, making it easy to understand and extend.
Automatic Module Registration
One of the key features of the Typus Development Framework is the automatic registration of modules. Modules are discovered and loaded dynamically at application startup, eliminating the need for manual registration. This is achieved through:
- Decorator-based registration: Modules, controllers, services, and repositories are annotated with decorators that register them with the dependency injection container.
- Dynamic module loading: The application scans the modules directory and loads modules based on the configuration in
config/modules.config.ts
. - Dependency resolution: Dependencies between modules are automatically resolved by the dependency injection container.
This automatic registration system simplifies the development process and reduces boilerplate code, allowing developers to focus on implementing business logic rather than configuring the application.
Module Architecture
The module architecture follows a layered approach, with clear separation between different responsibilities:
Directory Structure
Each module follows a standardized directory structure:
modules/feature/
├── FeatureModule.ts # Module definition
├── index.ts # Module exports
├── controllers/ # API controllers
│ └── FeatureController.ts
├── services/ # Business logic
│ └── FeatureService.ts
├── repositories/ # Data access (optional)
│ └── FeatureRepository.ts
├── validation/ # Request validation (optional)
│ └── featureSchemas.ts
├── dto/ # Data transfer objects (optional)
│ └── FeatureDto.ts
└── dsl/ # DSL models (optional)
└── feature.model.ts
Module Components
Module Class
The module class is the entry point for the module. It extends BaseModule
and is responsible for:
- Defining the module's base path
- Initializing controllers and services
- Setting up routes
- Configuring middleware
- Implementing module-specific initialization logic
Controller
Controllers handle HTTP requests and responses. They extend BaseController
and are responsible for:
- Defining API endpoints
- Validating request data
- Calling service methods
- Formatting responses
Service
Services implement business logic. They extend BaseService
and are responsible for:
- Implementing domain logic
- Handling data operations
- Managing transactions
- Throwing typed errors for exceptional conditions
Repository (Optional)
Repositories handle data access. They extend BaseRepository
and are responsible for:
- Implementing CRUD operations
- Handling data transformation
- Managing database-specific logic
Validation Schemas
Validation schemas define the structure and constraints for request data. They use Zod for schema validation.
Custom Logic Implementation
All custom business logic in the Typus Development Framework is implemented through modules. This approach ensures that:
- Separation of concerns: Each module is responsible for a specific domain or feature.
- Encapsulation: Module internals are hidden from other modules, promoting loose coupling.
- Testability: Modules can be tested in isolation.
- Reusability: Common functionality can be extracted into shared modules.
- Maintainability: Changes to one module do not affect other modules.
By implementing custom logic through modules, the framework provides a consistent and scalable approach to application development. Developers can focus on implementing business requirements without worrying about the underlying infrastructure.
Decorator-Based Architecture
The framework uses decorators to simplify component registration and configuration:
Module Decorator
@Module({ path: 'feature' })
export class FeatureModule extends BaseModule {
// Module implementation
}
Controller Decorator
@Controller({ path: 'features' })
export class FeatureController extends BaseController {
// Controller implementation
}
Service Decorator
@Service()
export class FeatureService extends BaseService {
// Service implementation
}
Repository Decorator
@Repository()
export class FeatureRepository extends BaseRepository<Feature> {
// Repository implementation
}
These decorators automatically register components with the dependency injection container, eliminating the need for manual registration.
Route Definition
Routes are defined in the module's initializeRoutes
method, which sets up the API endpoints for the module. The framework provides built-in support for:
- HTTP method handlers (GET, POST, PUT, DELETE, etc.)
- Middleware integration (authentication, validation, etc.)
- Parameter validation
- Response formatting
Authentication and Authorization
Authentication and authorization are handled through middleware provided by the BaseModule
class:
- Authentication middleware ensures that requests are authenticated
- Role-based authorization middleware restricts access based on user roles
- Permission-based authorization middleware restricts access based on user permissions
Validation
Request validation is handled through middleware provided by the BaseController
class, which uses Zod schemas to validate request data.
Error Handling
The framework uses a centralized error handling approach with typed errors, which are automatically converted to appropriate HTTP responses.
Dependency Injection
The framework uses the tsyringe container for dependency injection, which automatically resolves dependencies between components.
Module Communication
Modules can communicate with each other through dependency injection, allowing for loose coupling between modules.
Module Extension
Modules can be extended by adding new components or modifying existing ones, providing a flexible approach to application development.
Best Practices
Module Design
- Single Responsibility: Each module should focus on a specific domain or feature
- Clear Boundaries: Define clear boundaries between modules
- Minimal Dependencies: Minimize dependencies between modules
- Consistent Structure: Follow the standard module structure
- Descriptive Naming: Use descriptive names for modules, controllers, services, etc.
Controllers
- Thin Controllers: Keep controllers thin, focusing on request/response handling
- Validation: Always validate request data
- Error Handling: Let the framework handle errors
- Response Formatting: Use the standard response methods
Services
- Business Logic: Put all business logic in services
- Typed Errors: Use typed errors for exceptional conditions
- Transactions: Use transactions for operations that modify multiple records
- Logging: Log important operations and errors
Repositories
- Data Access: Put all data access logic in repositories
- Reusable Queries: Create reusable query methods
- Data Transformation: Handle data transformation in repositories
- Logging: Log database operations
Validation
- Comprehensive Schemas: Define comprehensive validation schemas
- Reusable Schemas: Create reusable schema components
- Clear Error Messages: Provide clear error messages
- Type Inference: Use type inference for DTOs
Conclusion
The module structure and patterns used in the Typus Development Framework provide a solid foundation for building scalable, maintainable, and extensible applications. By following these patterns, developers can create consistent modules that integrate seamlessly with the framework.
The automatic registration system, combined with the decorator-based architecture, reduces boilerplate code and allows developers to focus on implementing business logic. The clear separation of concerns between controllers, services, and repositories promotes code reusability and maintainability.