模块定义
Entity Engine 的模块定义提供了标准化的模块结构和声明方式。通过清晰的模块定义,可以实现模块化开发、依赖管理和功能扩展。每个模块都需要实现 IEntityModule
接口,包含基本信息和三个生命周期方法。
模块基本结构
1. 模块信息定义
每个模块都需要定义基本信息,包含模块的标识、元数据和依赖关系:
import type { EntityModuleInfo, ModuleDependency } from '@scenemesh/entity-engine';
const moduleInfo: EntityModuleInfo = {
// 基本信息
name: 'my-module', // 模块唯一名称
version: '1.0.0', // 语义化版本号
description: '模块功能描述', // 模块说明
author: 'Your Name', // 作者信息
// 依赖声明
dependencies: [
{ name: 'core-module', version: '^1.0.0' },
{ name: 'utils-module', version: '>=2.0.0', optional: true }
]
};
2. 模块类实现
import type { IEntityModule } from '@scenemesh/entity-engine';
export class MyModule implements IEntityModule {
readonly info = moduleInfo;
async setupConfig(args) {
// 配置阶段:设置模块配置和验证
}
async setupComponents(args) {
// 组件阶段:注册服务、组件和处理器
}
async setupData(args) {
// 数据阶段:执行数据迁移和初始化
}
}
模块信息字段详解
基础标识信息
const moduleInfo: EntityModuleInfo = {
name: 'user-management', // 模块名称,必须唯一
version: '2.1.0', // 版本号,遵循 semver 规范
description: '用户管理模块', // 功能描述
displayName: '用户管理', // 显示名称(可选)
author: 'Development Team', // 作者信息
license: 'MIT', // 许可证(可选)
homepage: 'https://...', // 主页地址(可选)
keywords: ['user', 'auth'] // 关键词(可选)
};
依赖声明
通过 dependencies
数组声明模块依赖关系:
const moduleInfo: EntityModuleInfo = {
name: 'advanced-module',
version: '1.0.0',
dependencies: [
// 必需依赖
{ name: 'core-services', version: '^1.0.0' },
// 精确版本依赖
{ name: 'database-module', version: '2.1.0' },
// 版本范围依赖
{ name: 'auth-module', version: '>=1.5.0 <2.0.0' },
// 可选依赖
{ name: 'email-service', version: '^1.0.0', optional: true },
// 带说明的依赖
{
name: 'payment-gateway',
version: '^2.0.0',
optional: true,
reason: '用于处理在线支付功能'
}
]
};
生命周期方法详解
setupConfig 方法
在配置阶段设置和验证模块配置:
async setupConfig({ configManager, logger }) {
// 设置默认配置
configManager.setDefault(this.info.name, {
apiEndpoint: 'https://api.example.com',
timeout: 5000,
retryAttempts: 3,
enableCache: true
});
// 获取和验证配置
const config = configManager.get(this.info.name);
// 必要的配置验证
if (!config.apiEndpoint) {
throw new Error('API endpoint is required');
}
if (config.timeout < 1000) {
logger.warn('Timeout is too low, using minimum value');
configManager.set(`${this.info.name}.timeout`, 1000);
}
// 环境特定配置
if (process.env.NODE_ENV === 'development') {
configManager.merge(this.info.name, {
debug: true,
logLevel: 'debug'
});
}
}
setupComponents 方法
在组件阶段注册服务、组件和处理器:
async setupComponents({
serviceRegistry,
componentRegistry,
configManager,
logger
}) {
const config = configManager.get(this.info.name);
// 注册主要服务
serviceRegistry.register('ApiService', new ApiService({
endpoint: config.apiEndpoint,
timeout: config.timeout
}));
// 注册依赖其他服务的服务
const dbService = serviceRegistry.get('DatabaseService');
const cacheService = serviceRegistry.get('CacheService');
serviceRegistry.register('UserService',
new UserService(dbService, cacheService)
);
// 注册组件
componentRegistry.registerView(new UserManagementView());
componentRegistry.registerAdapter(new UserDataAdapter());
// 注册事件处理器
const eventRegistry = serviceRegistry.get('EventRegistry');
eventRegistry.on('user.created', this.handleUserCreated.bind(this));
logger.info(`${this.info.name} components registered successfully`);
}
setupData 方法
在数据阶段执行数据初始化和迁移:
async setupData({ serviceRegistry, logger }) {
const dbService = serviceRegistry.get('DatabaseService');
try {
// 检查数据表是否存在
const tablesExist = await dbService.checkTables(['users', 'user_roles']);
if (!tablesExist) {
// 创建数据表
await dbService.createTable('users', {
id: { type: 'string', primaryKey: true },
username: { type: 'string', unique: true },
email: { type: 'string', unique: true },
passwordHash: 'string',
createdAt: { type: 'datetime', default: 'now()' },
updatedAt: { type: 'datetime', default: 'now()' }
});
await dbService.createTable('user_roles', {
id: { type: 'string', primaryKey: true },
userId: { type: 'string', foreignKey: 'users.id' },
role: 'string',
grantedAt: { type: 'datetime', default: 'now()' }
});
logger.info('Database tables created successfully');
}
// 执行数据迁移
await this.runDataMigrations(dbService);
// 创建默认数据
await this.createDefaultData(serviceRegistry);
} catch (error) {
logger.error(`Data initialization failed: ${error.message}`);
throw error;
}
}
private async runDataMigrations(dbService) {
// 执行版本迁移脚本
const migrations = [
{ version: '1.0.1', script: 'add_user_status_column.sql' },
{ version: '1.1.0', script: 'add_user_preferences_table.sql' }
];
for (const migration of migrations) {
if (await dbService.needsMigration(migration.version)) {
await dbService.executeMigration(migration.script);
await dbService.recordMigration(migration.version);
}
}
}
private async createDefaultData(serviceRegistry) {
const userService = serviceRegistry.get('UserService');
// 检查是否需要创建默认管理员
const adminExists = await userService.findByUsername('admin');
if (!adminExists) {
await userService.createDefaultAdmin({
username: 'admin',
email: 'admin@example.com',
password: 'temp-password-change-me'
});
}
}
完整模块示例
基础模块示例
import type { IEntityModule, EntityModuleInfo } from '@scenemesh/entity-engine';
const moduleInfo: EntityModuleInfo = {
name: 'notification-service',
version: '1.0.0',
description: '通知服务模块,支持邮件、短信和推送通知',
author: 'Notification Team',
dependencies: [
{ name: 'core-services', version: '^1.0.0' },
{ name: 'user-management', version: '^2.0.0' }
]
};
export class NotificationModule implements IEntityModule {
readonly info = moduleInfo;
async setupConfig({ configManager, logger }) {
// 设置通知服务配置
configManager.setDefault(this.info.name, {
email: {
provider: 'smtp',
host: 'localhost',
port: 587,
secure: false,
from: 'noreply@example.com'
},
sms: {
provider: 'twilio',
enabled: false
},
push: {
enabled: true,
batchSize: 100
}
});
const config = configManager.get(this.info.name);
// 验证邮件配置
if (!config.email.host) {
throw new Error('Email host is required');
}
logger.info('Notification module configured');
}
async setupComponents({ serviceRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 注册邮件服务
serviceRegistry.register('EmailService',
new EmailService(config.email)
);
// 注册短信服务(如果启用)
if (config.sms.enabled) {
serviceRegistry.register('SmsService',
new SmsService(config.sms)
);
}
// 注册推送通知服务
if (config.push.enabled) {
serviceRegistry.register('PushNotificationService',
new PushNotificationService(config.push)
);
}
// 注册通知管理器
const userService = serviceRegistry.get('UserService');
serviceRegistry.register('NotificationManager',
new NotificationManager(userService, {
emailService: serviceRegistry.get('EmailService'),
smsService: serviceRegistry.get('SmsService'),
pushService: serviceRegistry.get('PushNotificationService')
})
);
logger.info('Notification services registered');
}
async setupData({ serviceRegistry, logger }) {
const dbService = serviceRegistry.get('DatabaseService');
// 创建通知相关数据表
await dbService.ensureTable('notifications', {
id: { type: 'string', primaryKey: true },
userId: { type: 'string', foreignKey: 'users.id' },
type: { type: 'string', enum: ['email', 'sms', 'push'] },
title: 'string',
content: 'text',
status: { type: 'string', enum: ['pending', 'sent', 'failed'], default: 'pending' },
createdAt: { type: 'datetime', default: 'now()' },
sentAt: { type: 'datetime', nullable: true }
});
await dbService.ensureTable('notification_templates', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', unique: true },
type: { type: 'string', enum: ['email', 'sms', 'push'] },
subject: { type: 'string', nullable: true },
content: 'text',
variables: { type: 'json', nullable: true }
});
// 创建默认通知模板
await this.createDefaultTemplates(dbService);
logger.info('Notification data initialized');
}
private async createDefaultTemplates(dbService) {
const templates = [
{
name: 'welcome_email',
type: 'email',
subject: '欢迎使用我们的服务',
content: '亲爱的 {{username}},欢迎注册我们的服务!',
variables: ['username']
},
{
name: 'password_reset',
type: 'email',
subject: '密码重置请求',
content: '请点击以下链接重置您的密码:{{resetLink}}',
variables: ['resetLink']
}
];
for (const template of templates) {
const exists = await dbService.findOne('notification_templates', { name: template.name });
if (!exists) {
await dbService.insert('notification_templates', template);
}
}
}
}
复杂模块示例
const blogModuleInfo: EntityModuleInfo = {
name: 'blog-system',
version: '2.0.0',
description: '博客系统模块,支持文章发布、评论和分类管理',
author: 'Blog Team',
dependencies: [
{ name: 'user-management', version: '^2.0.0' },
{ name: 'file-upload', version: '^1.0.0' },
{ name: 'search-engine', version: '^1.0.0', optional: true }
]
};
export class BlogModule implements IEntityModule {
readonly info = blogModuleInfo;
async setupConfig({ configManager, logger }) {
configManager.setDefault(this.info.name, {
posts: {
defaultStatus: 'draft',
autoSave: true,
autoSaveInterval: 30000,
maxTitleLength: 100,
maxContentLength: 50000
},
comments: {
enabled: true,
moderation: true,
maxLength: 1000,
allowAnonymous: false
},
categories: {
maxDepth: 3,
allowMultiple: true
},
seo: {
generateSlugs: true,
generateSitemap: true,
enableRichSnippets: true
}
});
// 验证配置
const config = configManager.get(this.info.name);
if (config.posts.maxContentLength < 1000) {
logger.warn('Content length limit is too low, increasing to minimum');
configManager.set(`${this.info.name}.posts.maxContentLength`, 1000);
}
}
async setupComponents({ serviceRegistry, componentRegistry, configManager, logger }) {
const config = configManager.get(this.info.name);
// 获取依赖服务
const dbService = serviceRegistry.get('DatabaseService');
const userService = serviceRegistry.get('UserService');
const fileService = serviceRegistry.get('FileUploadService');
// 注册博客核心服务
serviceRegistry.register('PostService',
new PostService(dbService, userService, config.posts)
);
serviceRegistry.register('CategoryService',
new CategoryService(dbService, config.categories)
);
if (config.comments.enabled) {
serviceRegistry.register('CommentService',
new CommentService(dbService, userService, config.comments)
);
}
// 注册SEO服务
if (config.seo.generateSlugs || config.seo.generateSitemap) {
serviceRegistry.register('BlogSeoService',
new BlogSeoService(dbService, config.seo)
);
}
// 注册搜索服务(如果搜索引擎模块可用)
const searchService = serviceRegistry.get('SearchService');
if (searchService) {
serviceRegistry.register('BlogSearchService',
new BlogSearchService(searchService)
);
}
// 注册UI组件
componentRegistry.registerView(new BlogListView());
componentRegistry.registerView(new BlogPostView());
componentRegistry.registerView(new BlogAdminView());
// 注册数据适配器
componentRegistry.registerAdapter(new BlogDataAdapter());
logger.info('Blog system components registered');
}
async setupData({ serviceRegistry, logger }) {
const dbService = serviceRegistry.get('DatabaseService');
// 创建博客相关数据表
await this.createBlogTables(dbService);
// 创建默认分类
await this.createDefaultCategories(serviceRegistry);
// 初始化SEO数据
await this.initializeSeoData(serviceRegistry);
logger.info('Blog system data initialized');
}
private async createBlogTables(dbService) {
// 文章表
await dbService.ensureTable('blog_posts', {
id: { type: 'string', primaryKey: true },
title: { type: 'string', maxLength: 100 },
slug: { type: 'string', unique: true },
content: { type: 'text', maxLength: 50000 },
excerpt: { type: 'text', maxLength: 500, nullable: true },
authorId: { type: 'string', foreignKey: 'users.id' },
status: { type: 'string', enum: ['draft', 'published', 'archived'], default: 'draft' },
publishedAt: { type: 'datetime', nullable: true },
createdAt: { type: 'datetime', default: 'now()' },
updatedAt: { type: 'datetime', default: 'now()' },
viewCount: { type: 'integer', default: 0 },
metadata: { type: 'json', nullable: true }
});
// 分类表
await dbService.ensureTable('blog_categories', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', maxLength: 50 },
slug: { type: 'string', unique: true },
description: { type: 'text', nullable: true },
parentId: { type: 'string', foreignKey: 'blog_categories.id', nullable: true },
order: { type: 'integer', default: 0 },
createdAt: { type: 'datetime', default: 'now()' }
});
// 文章分类关联表
await dbService.ensureTable('blog_post_categories', {
postId: { type: 'string', foreignKey: 'blog_posts.id' },
categoryId: { type: 'string', foreignKey: 'blog_categories.id' }
});
// 评论表
await dbService.ensureTable('blog_comments', {
id: { type: 'string', primaryKey: true },
postId: { type: 'string', foreignKey: 'blog_posts.id' },
authorId: { type: 'string', foreignKey: 'users.id', nullable: true },
authorName: { type: 'string', nullable: true },
authorEmail: { type: 'string', nullable: true },
content: { type: 'text', maxLength: 1000 },
status: { type: 'string', enum: ['pending', 'approved', 'rejected'], default: 'pending' },
parentId: { type: 'string', foreignKey: 'blog_comments.id', nullable: true },
createdAt: { type: 'datetime', default: 'now()' },
ipAddress: { type: 'string', nullable: true }
});
// 标签表
await dbService.ensureTable('blog_tags', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', unique: true, maxLength: 30 },
slug: { type: 'string', unique: true },
color: { type: 'string', nullable: true }
});
// 文章标签关联表
await dbService.ensureTable('blog_post_tags', {
postId: { type: 'string', foreignKey: 'blog_posts.id' },
tagId: { type: 'string', foreignKey: 'blog_tags.id' }
});
}
private async createDefaultCategories(serviceRegistry) {
const categoryService = serviceRegistry.get('CategoryService');
const defaultCategories = [
{ name: '技术', slug: 'tech', description: '技术相关文章' },
{ name: '生活', slug: 'life', description: '生活感悟和分享' },
{ name: '随笔', slug: 'notes', description: '随笔记录' }
];
for (const category of defaultCategories) {
const exists = await categoryService.findBySlug(category.slug);
if (!exists) {
await categoryService.create(category);
}
}
}
private async initializeSeoData(serviceRegistry) {
const seoService = serviceRegistry.get('BlogSeoService');
if (seoService) {
await seoService.initializeDefaultSettings({
siteName: 'My Blog',
siteDescription: 'A blog powered by Entity Engine',
defaultImage: '/images/blog-default.jpg'
});
}
}
}
模块验证与调试
配置验证
async setupConfig({ configManager, logger }) {
// 设置配置Schema进行验证
const schema = {
type: 'object',
properties: {
apiUrl: { type: 'string', format: 'uri' },
timeout: { type: 'number', minimum: 1000 },
retryCount: { type: 'number', minimum: 0, maximum: 5 }
},
required: ['apiUrl']
};
configManager.setSchema(this.info.name, schema);
try {
const config = configManager.get(this.info.name);
configManager.validate(this.info.name, config);
logger.info('Configuration validated successfully');
} catch (error) {
logger.error('Configuration validation failed:', error.message);
throw error;
}
}
依赖检查
async setupComponents({ serviceRegistry, logger }) {
// 检查必需依赖
const requiredServices = ['DatabaseService', 'UserService'];
for (const serviceName of requiredServices) {
if (!serviceRegistry.has(serviceName)) {
throw new Error(`Required service not available: ${serviceName}`);
}
}
// 检查可选依赖
const optionalServices = ['CacheService', 'LoggingService'];
for (const serviceName of optionalServices) {
if (serviceRegistry.has(serviceName)) {
logger.info(`Optional service available: ${serviceName}`);
} else {
logger.warn(`Optional service not available: ${serviceName}`);
}
}
}
错误处理
async setupData({ serviceRegistry, logger }) {
try {
await this.initializeData(serviceRegistry);
logger.info('Data initialization completed successfully');
} catch (error) {
logger.error('Data initialization failed:', error);
// 提供详细的错误信息
if (error.code === 'TABLE_NOT_EXISTS') {
logger.error('Please ensure database module is properly configured');
} else if (error.code === 'PERMISSION_DENIED') {
logger.error('Database permissions insufficient');
}
// 重新抛出错误以阻止模块加载
throw new Error(`${this.info.name} initialization failed: ${error.message}`);
}
}
最佳实践
1. 模块命名规范
- 使用小写字母和连字符分隔
- 名称应该清晰地表达模块功能
- 避免使用通用或容易混淆的名称
// 好的命名
'user-management'
'blog-system'
'payment-gateway'
'notification-service'
// 避免的命名
'utils'
'common'
'module1'
'temp'
2. 版本管理
- 遵循语义化版本规范
- 在配置中使用适当的版本约束
- 及时更新依赖版本
const moduleInfo: EntityModuleInfo = {
name: 'my-module',
version: '1.2.3', // 主版本.次版本.修订版本
dependencies: [
{ name: 'core', version: '^1.0.0' }, // 兼容版本
{ name: 'utils', version: '~1.2.0' }, // 近似版本
{ name: 'specific', version: '2.1.0' } // 精确版本
]
};
3. 依赖管理
- 明确区分必需依赖和可选依赖
- 避免循环依赖
- 合理使用依赖注入
dependencies: [
// 核心依赖
{ name: 'database', version: '^1.0.0' },
// 可选功能依赖
{ name: 'cache', version: '^1.0.0', optional: true },
// 带说明的依赖
{
name: 'email-service',
version: '^1.0.0',
optional: true,
reason: '用于发送通知邮件,如不需要邮件功能可以不安装'
}
]
4. 配置管理
- 提供合理的默认配置
- 支持环境特定配置
- 进行配置验证
5. 错误处理
- 提供清晰的错误信息
- 区分致命错误和警告
- 支持优雅降级
下一步
了解模块定义后,继续学习:
Last updated on