Feathers.js 服务详解 - 从内存到数据库的完整实现
发布时间:2024-05-20
作者:一介布衣
标签:Feathers.js, Services, 数据库适配器, 服务架构
前言
上一篇文章我们通过待办事项项目体验了 Feathers.js 的强大功能,今天咱们来深入学习 Services(服务)的各种实现方式。说实话,Service 是 Feathers.js 的核心,理解了 Service 就理解了 Feathers.js 的精髓。
我记得刚开始学 Feathers.js 的时候,总觉得 Service 就是简单的 CRUD 操作。后来深入使用才发现,Feathers.js 的 Service 设计非常巧妙,它不仅统一了不同数据源的操作接口,还提供了强大的扩展能力。无论数据存储在内存、文件、数据库,还是来自第三方 API,都可以用同样的方式操作。
今天我就带大家从最简单的内存服务开始,一步步学习各种 Service 的实现方式。
Service 的本质
什么是 Service?
Service 在 Feathers.js 中是一个抽象概念,它代表一个数据资源。每个 Service 都实现了标准的 CRUD 接口:
javascript
// Service 的标准接口
interface Service {
find(params) // 查询多条记录
get(id, params) // 获取单条记录
create(data, params) // 创建记录
update(id, data, params) // 完全更新记录
patch(id, data, params) // 部分更新记录
remove(id, params) // 删除记录
}
Service 的统一性
这种统一的接口设计带来了巨大的好处:
javascript
// 无论底层是什么存储,客户端代码都一样
const users = app.service('users');
// 这些操作在任何 Service 中都是一样的
await users.find();
await users.get(1);
await users.create({ name: 'John' });
await users.patch(1, { name: 'Jane' });
await users.remove(1);
内存服务(Memory Service)
1. 基础内存服务
最简单的 Service 就是内存服务,数据存储在内存中:
javascript
// src/services/memory-demo/memory-demo.class.js
class MemoryService {
constructor() {
this.data = [];
this.currentId = 0;
}
async find(params) {
const { query = {} } = params;
let result = [...this.data];
// 简单过滤
Object.keys(query).forEach(key => {
if (key.startsWith('$')) return; // 跳过特殊查询参数
result = result.filter(item => {
if (typeof query[key] === 'object') {
// 处理复杂查询(如范围查询)
return this.matchComplexQuery(item[key], query[key]);
}
return item[key] === query[key];
});
});
// 排序
if (query.$sort) {
result = this.sortData(result, query.$sort);
}
// 分页
const total = result.length;
const limit = parseInt(query.$limit) || total;
const skip = parseInt(query.$skip) || 0;
result = result.slice(skip, skip + limit);
return {
total,
limit,
skip,
data: result
};
}
async get(id, params) {
const item = this.data.find(d => d.id === parseInt(id));
if (!item) {
throw new Error(`记录 \${id} 不存在`);
}
return item;
}
async create(data, params) {
// 支持批量创建
if (Array.isArray(data)) {
return Promise.all(data.map(item => this.create(item, params)));
}
const item = {
id: ++this.currentId,
...data,
createdAt: new Date(),
updatedAt: new Date()
};
this.data.push(item);
return item;
}
async update(id, data, params) {
const index = this.data.findIndex(d => d.id === parseInt(id));
if (index === -1) {
throw new Error(`记录 \${id} 不存在`);
}
// 完全替换
const item = {
id: parseInt(id),
...data,
updatedAt: new Date()
};
this.data[index] = item;
return item;
}
async patch(id, data, params) {
const item = await this.get(id, params);
// 部分更新
Object.assign(item, data, {
updatedAt: new Date()
});
return item;
}
async remove(id, params) {
const index = this.data.findIndex(d => d.id === parseInt(id));
if (index === -1) {
throw new Error(`记录 \${id} 不存在`);
}
const item = this.data[index];
this.data.splice(index, 1);
return item;
}
// 辅助方法
matchComplexQuery(value, query) {
if (query.$gt !== undefined) return value > query.$gt;
if (query.$gte !== undefined) return value >= query.$gte;
if (query.$lt !== undefined) return value < query.$lt;
if (query.$lte !== undefined) return value <= query.$lte;
if (query.$ne !== undefined) return value !== query.$ne;
if (query.$in !== undefined) return query.$in.includes(value);
if (query.$nin !== undefined) return !query.$nin.includes(value);
return true;
}
sortData(data, sortQuery) {
return data.sort((a, b) => {
for (const [field, direction] of Object.entries(sortQuery)) {
const aVal = a[field];
const bVal = b[field];
if (aVal < bVal) return direction === 1 ? -1 : 1;
if (aVal > bVal) return direction === 1 ? 1 : -1;
}
return 0;
});
}
}
module.exports = MemoryService;
2. 使用官方内存适配器
Feathers.js 提供了官方的内存适配器:
javascript
// 安装内存适配器
npm install @feathersjs/memory
// 使用内存适配器
const { MemoryService } = require('@feathersjs/memory');
// 注册服务
app.use('todos', new MemoryService({
multi: true, // 允许批量操作
id: 'id', // 主键字段
startId: 1, // 起始ID
store: {}, // 自定义存储对象
paginate: {
default: 10,
max: 50
}
}));
3. 高级内存服务
javascript
// 带缓存和持久化的内存服务
class AdvancedMemoryService extends MemoryService {
constructor(options = {}) {
super();
this.options = options;
this.cacheTimeout = options.cacheTimeout || 60000; // 1分钟
this.persistFile = options.persistFile;
// 从文件加载数据
this.loadFromFile();
// 定期保存到文件
if (this.persistFile) {
setInterval(() => this.saveToFile(), 30000); // 30秒保存一次
}
}
async create(data, params) {
const result = await super.create(data, params);
// 触发保存
if (this.persistFile) {
this.saveToFile();
}
return result;
}
async patch(id, data, params) {
const result = await super.patch(id, data, params);
// 触发保存
if (this.persistFile) {
this.saveToFile();
}
return result;
}
async remove(id, params) {
const result = await super.remove(id, params);
// 触发保存
if (this.persistFile) {
this.saveToFile();
}
return result;
}
loadFromFile() {
if (!this.persistFile) return;
try {
const fs = require('fs');
if (fs.existsSync(this.persistFile)) {
const fileData = fs.readFileSync(this.persistFile, 'utf8');
const parsed = JSON.parse(fileData);
this.data = parsed.data || [];
this.currentId = parsed.currentId || 0;
}
} catch (error) {
console.error('加载数据文件失败:', error);
}
}
saveToFile() {
if (!this.persistFile) return;
try {
const fs = require('fs');
const dataToSave = {
data: this.data,
currentId: this.currentId,
savedAt: new Date()
};
fs.writeFileSync(this.persistFile, JSON.stringify(dataToSave, null, 2));
} catch (error) {
console.error('保存数据文件失败:', error);
}
}
// 数据统计
getStats() {
return {
totalRecords: this.data.length,
memoryUsage: JSON.stringify(this.data).length,
lastModified: Math.max(...this.data.map(d => new Date(d.updatedAt).getTime()))
};
}
// 批量导入
async bulkImport(records) {
const results = [];
for (const record of records) {
try {
const created = await this.create(record);
results.push({ success: true, data: created });
} catch (error) {
results.push({ success: false, error: error.message, data: record });
}
}
return results;
}
// 清空数据
async clear() {
this.data = [];
this.currentId = 0;
if (this.persistFile) {
this.saveToFile();
}
return { message: '数据已清空' };
}
}
文件系统服务
1. 基于文件的服务
javascript
// src/services/file-service/file-service.class.js
const fs = require('fs').promises;
const path = require('path');
class FileService {
constructor(options = {}) {
this.dataDir = options.dataDir || './data';
this.extension = options.extension || '.json';
this.ensureDataDir();
}
async ensureDataDir() {
try {
await fs.mkdir(this.dataDir, { recursive: true });
} catch (error) {
console.error('创建数据目录失败:', error);
}
}
getFilePath(id) {
return path.join(this.dataDir, `\${id}\${this.extension}`);
}
async find(params) {
const { query = {} } = params;
try {
const files = await fs.readdir(this.dataDir);
const jsonFiles = files.filter(f => f.endsWith(this.extension));
const data = [];
for (const file of jsonFiles) {
try {
const filePath = path.join(this.dataDir, file);
const content = await fs.readFile(filePath, 'utf8');
const record = JSON.parse(content);
// 简单过滤
if (this.matchesQuery(record, query)) {
data.push(record);
}
} catch (error) {
console.error(`读取文件 \${file} 失败:`, error);
}
}
// 排序
if (query.$sort) {
data.sort((a, b) => {
for (const [field, direction] of Object.entries(query.$sort)) {
if (a[field] < b[field]) return direction === 1 ? -1 : 1;
if (a[field] > b[field]) return direction === 1 ? 1 : -1;
}
return 0;
});
}
// 分页
const total = data.length;
const limit = parseInt(query.$limit) || total;
const skip = parseInt(query.$skip) || 0;
return {
total,
limit,
skip,
data: data.slice(skip, skip + limit)
};
} catch (error) {
throw new Error(`查询失败: \${error.message}`);
}
}
async get(id, params) {
const filePath = this.getFilePath(id);
try {
const content = await fs.readFile(filePath, 'utf8');
return JSON.parse(content);
} catch (error) {
if (error.code === 'ENOENT') {
throw new Error(`记录 \${id} 不存在`);
}
throw new Error(`读取记录失败: \${error.message}`);
}
}
async create(data, params) {
const id = data.id || this.generateId();
const record = {
...data,
id,
createdAt: new Date(),
updatedAt: new Date()
};
const filePath = this.getFilePath(id);
try {
await fs.writeFile(filePath, JSON.stringify(record, null, 2));
return record;
} catch (error) {
throw new Error(`创建记录失败: \${error.message}`);
}
}
async update(id, data, params) {
const record = {
...data,
id,
updatedAt: new Date()
};
const filePath = this.getFilePath(id);
try {
await fs.writeFile(filePath, JSON.stringify(record, null, 2));
return record;
} catch (error) {
throw new Error(`更新记录失败: \${error.message}`);
}
}
async patch(id, data, params) {
const existing = await this.get(id, params);
const updated = {
...existing,
...data,
id,
updatedAt: new Date()
};
const filePath = this.getFilePath(id);
try {
await fs.writeFile(filePath, JSON.stringify(updated, null, 2));
return updated;
} catch (error) {
throw new Error(`更新记录失败: \${error.message}`);
}
}
async remove(id, params) {
const record = await this.get(id, params);
const filePath = this.getFilePath(id);
try {
await fs.unlink(filePath);
return record;
} catch (error) {
throw new Error(`删除记录失败: \${error.message}`);
}
}
generateId() {
return Date.now().toString() + Math.random().toString(36).substr(2, 9);
}
matchesQuery(record, query) {
for (const [key, value] of Object.entries(query)) {
if (key.startsWith('$')) continue;
if (record[key] !== value) {
return false;
}
}
return true;
}
// 备份数据
async backup(backupPath) {
const data = await this.find({ query: {} });
await fs.writeFile(backupPath, JSON.stringify(data, null, 2));
return { message: `备份完成,共 \${data.total} 条记录` };
}
// 恢复数据
async restore(backupPath) {
try {
const content = await fs.readFile(backupPath, 'utf8');
const backup = JSON.parse(content);
let restored = 0;
for (const record of backup.data) {
try {
await this.create(record);
restored++;
} catch (error) {
console.error(`恢复记录 \${record.id} 失败:`, error);
}
}
return { message: `恢复完成,共恢复 \${restored} 条记录` };
} catch (error) {
throw new Error(`恢复失败: \${error.message}`);
}
}
}
module.exports = FileService;
数据库服务基础
1. 抽象数据库服务
javascript
// src/services/abstract-db/abstract-db.class.js
class AbstractDatabaseService {
constructor(options = {}) {
this.Model = options.Model;
this.id = options.id || 'id';
this.paginate = options.paginate;
}
async find(params) {
throw new Error('find 方法必须被子类实现');
}
async get(id, params) {
throw new Error('get 方法必须被子类实现');
}
async create(data, params) {
throw new Error('create 方法必须被子类实现');
}
async update(id, data, params) {
throw new Error('update 方法必须被子类实现');
}
async patch(id, data, params) {
throw new Error('patch 方法必须被子类实现');
}
async remove(id, params) {
throw new Error('remove 方法必须被子类实现');
}
// 通用的查询构建方法
buildQuery(query) {
const where = {};
const options = {};
Object.keys(query).forEach(key => {
if (key.startsWith('$')) {
// 处理特殊查询参数
switch (key) {
case '$limit':
options.limit = parseInt(query[key]);
break;
case '$skip':
options.offset = parseInt(query[key]);
break;
case '$sort':
options.order = this.buildSort(query[key]);
break;
case '$select':
options.attributes = query[key];
break;
}
} else {
// 处理普通字段查询
where[key] = this.buildFieldQuery(query[key]);
}
});
return { where, options };
}
buildFieldQuery(value) {
if (typeof value === 'object' && value !== null) {
// 处理复杂查询
const conditions = {};
Object.keys(value).forEach(operator => {
switch (operator) {
case '$gt':
conditions[this.getOperator('gt')] = value[operator];
break;
case '$gte':
conditions[this.getOperator('gte')] = value[operator];
break;
case '$lt':
conditions[this.getOperator('lt')] = value[operator];
break;
case '$lte':
conditions[this.getOperator('lte')] = value[operator];
break;
case '$ne':
conditions[this.getOperator('ne')] = value[operator];
break;
case '$in':
conditions[this.getOperator('in')] = value[operator];
break;
case '$nin':
conditions[this.getOperator('nin')] = value[operator];
break;
}
});
return conditions;
}
return value;
}
buildSort(sortQuery) {
// 子类实现具体的排序逻辑
return sortQuery;
}
getOperator(op) {
// 子类实现具体的操作符映射
return op;
}
// 分页处理
async paginate(query, countQuery) {
const { where, options } = this.buildQuery(query);
const [data, total] = await Promise.all([
this.executeQuery(where, options),
this.executeCount(countQuery || where)
]);
return {
total,
limit: options.limit || total,
skip: options.offset || 0,
data
};
}
async executeQuery(where, options) {
throw new Error('executeQuery 方法必须被子类实现');
}
async executeCount(where) {
throw new Error('executeCount 方法必须被子类实现');
}
// 事务支持
async transaction(callback) {
throw new Error('transaction 方法必须被子类实现');
}
// 批量操作
async bulkCreate(data, params) {
const results = [];
for (const item of data) {
try {
const created = await this.create(item, params);
results.push({ success: true, data: created });
} catch (error) {
results.push({ success: false, error: error.message, data: item });
}
}
return results;
}
async bulkUpdate(updates, params) {
const results = [];
for (const { id, data } of updates) {
try {
const updated = await this.patch(id, data, params);
results.push({ success: true, data: updated });
} catch (error) {
results.push({ success: false, error: error.message, id });
}
}
return results;
}
async bulkRemove(ids, params) {
const results = [];
for (const id of ids) {
try {
const removed = await this.remove(id, params);
results.push({ success: true, data: removed });
} catch (error) {
results.push({ success: false, error: error.message, id });
}
}
return results;
}
}
module.exports = AbstractDatabaseService;
自定义服务示例
1. 第三方 API 服务
javascript
// src/services/github-api/github-api.class.js
const axios = require('axios');
class GitHubApiService {
constructor(options = {}) {
this.token = options.token;
this.baseURL = 'https://api.github.com';
this.client = axios.create({
baseURL: this.baseURL,
headers: {
'Authorization': this.token ? `token \${this.token}` : undefined,
'Accept': 'application/vnd.github.v3+json'
}
});
}
async find(params) {
const { query = {} } = params;
const { q, sort = 'updated', order = 'desc' } = query;
if (!q) {
throw new Error('搜索查询参数 q 是必需的');
}
try {
const response = await this.client.get('/search/repositories', {
params: {
q,
sort,
order,
per_page: query.$limit || 30,
page: Math.floor((query.$skip || 0) / (query.$limit || 30)) + 1
}
});
return {
total: response.data.total_count,
limit: query.$limit || 30,
skip: query.$skip || 0,
data: response.data.items.map(this.transformRepository)
};
} catch (error) {
throw new Error(`GitHub API 请求失败: \${error.message}`);
}
}
async get(id, params) {
// id 格式: owner/repo
try {
const response = await this.client.get(`/repos/\${id}`);
return this.transformRepository(response.data);
} catch (error) {
if (error.response?.status === 404) {
throw new Error(`仓库 \${id} 不存在`);
}
throw new Error(`获取仓库信息失败: \${error.message}`);
}
}
async create(data, params) {
// 创建仓库
try {
const response = await this.client.post('/user/repos', {
name: data.name,
description: data.description,
private: data.private || false,
auto_init: data.auto_init || true
});
return this.transformRepository(response.data);
} catch (error) {
throw new Error(`创建仓库失败: \${error.message}`);
}
}
async patch(id, data, params) {
// 更新仓库信息
try {
const response = await this.client.patch(`/repos/\${id}`, {
name: data.name,
description: data.description,
private: data.private
});
return this.transformRepository(response.data);
} catch (error) {
throw new Error(`更新仓库失败: \${error.message}`);
}
}
async remove(id, params) {
// 删除仓库
try {
const repo = await this.get(id, params);
await this.client.delete(`/repos/\${id}`);
return repo;
} catch (error) {
throw new Error(`删除仓库失败: \${error.message}`);
}
}
// 不支持完全更新
async update(id, data, params) {
throw new Error('GitHub API 不支持完全更新操作,请使用 patch');
}
transformRepository(repo) {
return {
id: repo.full_name,
name: repo.name,
fullName: repo.full_name,
description: repo.description,
url: repo.html_url,
cloneUrl: repo.clone_url,
language: repo.language,
stars: repo.stargazers_count,
forks: repo.forks_count,
issues: repo.open_issues_count,
private: repo.private,
createdAt: repo.created_at,
updatedAt: repo.updated_at,
owner: {
login: repo.owner.login,
avatar: repo.owner.avatar_url,
url: repo.owner.html_url
}
};
}
// 获取仓库的提交历史
async getCommits(repoId, params = {}) {
try {
const response = await this.client.get(`/repos/\${repoId}/commits`, {
params: {
per_page: params.limit || 30,
page: params.page || 1,
since: params.since,
until: params.until
}
});
return response.data.map(commit => ({
sha: commit.sha,
message: commit.commit.message,
author: {
name: commit.commit.author.name,
email: commit.commit.author.email,
date: commit.commit.author.date
},
url: commit.html_url
}));
} catch (error) {
throw new Error(`获取提交历史失败: \${error.message}`);
}
}
// 获取仓库的问题列表
async getIssues(repoId, params = {}) {
try {
const response = await this.client.get(`/repos/\${repoId}/issues`, {
params: {
state: params.state || 'open',
per_page: params.limit || 30,
page: params.page || 1
}
});
return response.data.map(issue => ({
id: issue.id,
number: issue.number,
title: issue.title,
body: issue.body,
state: issue.state,
labels: issue.labels.map(label => label.name),
createdAt: issue.created_at,
updatedAt: issue.updated_at,
url: issue.html_url
}));
} catch (error) {
throw new Error(`获取问题列表失败: \${error.message}`);
}
}
}
module.exports = GitHubApiService;
2. 缓存服务
javascript
// src/services/cache/cache.class.js
const Redis = require('redis');
class CacheService {
constructor(options = {}) {
this.redis = Redis.createClient(options.redis || {});
this.prefix = options.prefix || 'cache:';
this.defaultTTL = options.defaultTTL || 3600; // 1小时
this.redis.on('error', (err) => {
console.error('Redis 连接错误:', err);
});
}
async find(params) {
const { query = {} } = params;
const pattern = query.pattern || '*';
const keys = await this.redis.keys(`\${this.prefix}\${pattern}`);
const data = [];
for (const key of keys) {
const value = await this.redis.get(key);
const ttl = await this.redis.ttl(key);
data.push({
id: key.replace(this.prefix, ''),
key: key,
value: this.parseValue(value),
ttl: ttl,
expiresAt: ttl > 0 ? new Date(Date.now() + ttl * 1000) : null
});
}
return {
total: data.length,
limit: query.$limit || data.length,
skip: query.$skip || 0,
data: data.slice(query.$skip || 0, (query.$skip || 0) + (query.$limit || data.length))
};
}
async get(id, params) {
const key = `\${this.prefix}\${id}`;
const value = await this.redis.get(key);
if (value === null) {
throw new Error(`缓存键 \${id} 不存在或已过期`);
}
const ttl = await this.redis.ttl(key);
return {
id,
key,
value: this.parseValue(value),
ttl,
expiresAt: ttl > 0 ? new Date(Date.now() + ttl * 1000) : null
};
}
async create(data, params) {
const { id, value, ttl = this.defaultTTL } = data;
const key = `\${this.prefix}\${id}`;
const serializedValue = this.serializeValue(value);
if (ttl > 0) {
await this.redis.setex(key, ttl, serializedValue);
} else {
await this.redis.set(key, serializedValue);
}
return {
id,
key,
value,
ttl,
expiresAt: ttl > 0 ? new Date(Date.now() + ttl * 1000) : null,
createdAt: new Date()
};
}
async patch(id, data, params) {
const existing = await this.get(id, params);
const { value, ttl } = data;
if (value !== undefined) {
const key = `\${this.prefix}\${id}`;
const serializedValue = this.serializeValue(value);
if (ttl !== undefined) {
if (ttl > 0) {
await this.redis.setex(key, ttl, serializedValue);
} else {
await this.redis.set(key, serializedValue);
}
} else {
// 保持原有的 TTL
await this.redis.set(key, serializedValue);
if (existing.ttl > 0) {
await this.redis.expire(key, existing.ttl);
}
}
} else if (ttl !== undefined) {
// 只更新 TTL
const key = `\${this.prefix}\${id}`;
if (ttl > 0) {
await this.redis.expire(key, ttl);
} else {
await this.redis.persist(key);
}
}
return await this.get(id, params);
}
async remove(id, params) {
const existing = await this.get(id, params);
const key = `\${this.prefix}\${id}`;
await this.redis.del(key);
return existing;
}
// 不支持完全更新
async update(id, data, params) {
return await this.patch(id, data, params);
}
serializeValue(value) {
if (typeof value === 'string') {
return value;
}
return JSON.stringify(value);
}
parseValue(value) {
try {
return JSON.parse(value);
} catch (error) {
return value;
}
}
// 批量设置
async mset(data) {
const pipeline = this.redis.pipeline();
for (const { id, value, ttl = this.defaultTTL } of data) {
const key = `\${this.prefix}\${id}`;
const serializedValue = this.serializeValue(value);
if (ttl > 0) {
pipeline.setex(key, ttl, serializedValue);
} else {
pipeline.set(key, serializedValue);
}
}
await pipeline.exec();
return { message: `批量设置 \${data.length} 个缓存项` };
}
// 批量获取
async mget(ids) {
const keys = ids.map(id => `\${this.prefix}\${id}`);
const values = await this.redis.mget(keys);
return ids.map((id, index) => ({
id,
value: values[index] ? this.parseValue(values[index]) : null,
exists: values[index] !== null
}));
}
// 清空所有缓存
async flush() {
const keys = await this.redis.keys(`\${this.prefix}*`);
if (keys.length > 0) {
await this.redis.del(keys);
}
return { message: `清空了 \${keys.length} 个缓存项` };
}
// 获取缓存统计
async getStats() {
const keys = await this.redis.keys(`\${this.prefix}*`);
const info = await this.redis.info('memory');
return {
totalKeys: keys.length,
memoryUsage: this.parseRedisInfo(info),
prefix: this.prefix
};
}
parseRedisInfo(info) {
const lines = info.split('\r\n');
const memory = {};
lines.forEach(line => {
if (line.includes(':')) {
const [key, value] = line.split(':');
if (key.includes('memory')) {
memory[key] = value;
}
}
});
return memory;
}
}
module.exports = CacheService;
服务注册和配置
1. 服务注册
javascript
// src/services/index.js
const users = require('./users/users.service.js');
const memoryDemo = require('./memory-demo/memory-demo.service.js');
const fileService = require('./file-service/file-service.service.js');
const githubApi = require('./github-api/github-api.service.js');
const cache = require('./cache/cache.service.js');
module.exports = function (app) {
// 注册所有服务
app.configure(users);
app.configure(memoryDemo);
app.configure(fileService);
app.configure(githubApi);
app.configure(cache);
};
2. 服务配置示例
javascript
// src/services/memory-demo/memory-demo.service.js
const MemoryService = require('./memory-demo.class.js');
const hooks = require('./memory-demo.hooks.js');
module.exports = function (app) {
const options = {
paginate: app.get('paginate'),
persistFile: './data/memory-demo.json'
};
// 注册服务
app.use('/memory-demo', new MemoryService(options));
// 获取服务实例
const service = app.service('memory-demo');
// 配置钩子
service.hooks(hooks);
// 添加自定义方法
service.getStats = function() {
return this.getStats();
};
service.bulkImport = function(data) {
return this.bulkImport(data);
};
service.clear = function() {
return this.clear();
};
};
总结
通过这篇文章,我们深入学习了 Feathers.js 中各种 Service 的实现方式:
✅ 内存服务:
- 基础内存服务实现
- 官方内存适配器使用
- 高级内存服务(缓存、持久化)
✅ 文件系统服务:
- 基于文件的数据存储
- 备份和恢复功能
- 文件系统操作优化
✅ 抽象数据库服务:
- 通用数据库服务基类
- 查询构建和分页处理
- 事务和批量操作支持
✅ 自定义服务:
- 第三方 API 集成
- 缓存服务实现
- 复杂业务逻辑封装
Service 是 Feathers.js 的核心,掌握了各种 Service 的实现方式,你就能够:
- 统一不同数据源的操作接口
- 灵活扩展业务功能
- 构建可维护的服务架构
- 实现复杂的数据处理逻辑
下一篇文章,我们将深入学习 Feathers.js 的钩子系统,看看如何优雅地组织业务逻辑。
相关文章推荐:
有问题欢迎留言讨论,我会及时回复大家!