在業務系統復雜度指數級增長的今天,服務層(Service Layer)的合理設計直接影響著系統的可維護性和擴展性。本文將深入剖析 Egg.js 框架中的服務層架構設計,從基礎實現到高級封裝,全方位講解企業級應用的開發實踐。
一、Service 層核心職責與依賴注入
1. Service 層定位原則
- 數據訪問代理:統一管理數據庫/第三方API調用
- 業務邏輯容器:封裝核心業務流程
- 事務協調中心:管理跨模型操作的事務邊界
- 復用基礎設施:集成緩存、消息隊列等公共服務
2. 依賴注入實現
通過 ctx.service
訪問服務實例:
// app/controller/user.js
async create() {const { ctx } = this;// 調用服務層方法const user = await ctx.service.user.createWithProfile(ctx.request.body);ctx.body = user;
}// app/service/user.js
class UserService extends Service {// 注入其他服務get profileService() {return this.ctx.service.profile;}async createWithProfile(data) {const user = await this.createUser(data);await this.profileService.initUserProfile(user.id);return user;}
}
依賴管理規范:
- 禁止服務層之間循環依賴
- 基礎服務通過
app.js
掛載到全局 - 敏感服務使用動態加載機制
二、業務邏輯復用模式
1. 基礎復用方案
(1) 繼承式復用
// app/core/base_service.js
class BaseService extends Service {async softDelete(id) {return this.ctx.model[this.modelName].update({ id }, { deleted_at: new Date() });}
}// app/service/article.js
class ArticleService extends BaseService {get modelName() { return 'Article'; }
}
(2) 組合式復用
// app/core/crud_operations.js
module.exports = {async bulkUpdate(ids, data) {return this.model.update({ id: { [Op.in]: ids } },{ where: data });}
};// app/service/product.js
const CrudOperations = require('../core/crud_operations');class ProductService extends Service {constructor(ctx) {super(ctx);Object.assign(this, CrudOperations);}
}
2. 高級復用模式
(1) 策略模式實現
// app/core/payment_strategies
class AlipayStrategy {async pay(amount) { /* 支付寶實現 */ }
}class WechatPayStrategy {async pay(amount) { /* 微信支付實現 */ }
}// app/service/payment.js
class PaymentService extends Service {async createPayment(type, amount) {const strategy = this.getStrategy(type);return strategy.pay(amount);}getStrategy(type) {const strategies = {alipay: new AlipayStrategy(this.ctx),wechat: new WechatPayStrategy(this.ctx)};return strategies[type];}
}
(2) 管道模式處理
// app/core/pipeline.js
class OrderPipeline {constructor() {this.steps = [];}addStep(step) {this.steps.push(step);}async execute(data) {return this.steps.reduce((promise, step) => promise.then(step),Promise.resolve(data));}
}// 使用示例
const pipeline = new OrderPipeline();
pipeline.addStep(validateStock).addStep(calculatePrice).addStep(createOrder);
await pipeline.execute(orderData);
三、事務處理與數據庫封裝
1. 自動事務管理
// app/core/transaction.js
module.exports = async function (ctx, fn) {const transaction = await ctx.model.transaction();try {const result = await fn(transaction);await transaction.commit();return result;} catch (err) {await transaction.rollback();throw err;}
};// 使用示例
await ctx.service.transaction(async (t) => {await serviceA.create(dataA, { transaction: t });await serviceB.update(dataB, { transaction: t });
});
優化建議:建立全局的異常捕獲機制
2. 數據庫操作封裝
// app/service/base.js
class BaseService extends Service {async findWithCache(key, queryFn, ttl = 60) {const cache = await this.app.redis.get(key);if (cache) return JSON.parse(cache);const data = await queryFn();await this.app.redis.setex(key, ttl, JSON.stringify(data));return data;}async paginate(model, options) {const { page = 1, pageSize = 15 } = options;const result = await model.findAndCountAll({offset: (page - 1) * pageSize,limit: pageSize,...options});return {data: result.rows,pagination: {page: Number(page),pageSize: Number(pageSize),total: result.count}};}
}
事務最佳實踐:
- 單個事務操作不超過5個SQL
- 事務內避免遠程HTTP調用
- 使用事務隔離級別控制
- 記錄事務日志用于審計
四、服務單元測試策略
1. 測試環境搭建
// test/service/user.test.js
const { app, assert } = require('egg-mock/bootstrap');describe('UserService', () => {let ctx;beforeEach(async () => {ctx = app.mockContext();await app.model.sync({ force: true });});it('should create user with profile', async () => {const service = ctx.service.user;const user = await service.createWithProfile({username: 'test',profile: { bio: 'developer' }});assert(user.id);const profile = await ctx.model.Profile.findOne({where: { userId: user.id }});assert(profile.bio === 'developer');});
});
2. 高級測試技巧
(1) 模擬外部依賴
app.mockService('payment', 'create', () => {return { status: 'success' };
});app.mockClassFunction('redis', 'get', async (key) => {return JSON.stringify({ cached: true });
});
(2) 事務回滾測試
it('should rollback when error occurs', async () => {await assert.rejects(async () => {await ctx.service.transaction(async t => {await createTestData(t);throw new Error('test rollback');});},{ message: 'test rollback' });const count = await ctx.model.Test.count();assert(count === 0);
});
測試覆蓋率優化:
- 核心服務保持100%行覆蓋
- 使用
nyc
生成覆蓋率報告 - 集成SonarQube進行質量檢測
- 添加Mutation Test(變異測試)
下篇預告:中間件開發與實戰應用
下一篇將深入探討:
- 中間件執行機制與洋蔥模型
- 編寫日志/權限/性能監控中間件
- 配置全局與路由級中間件
- 常見中間件開發陷阱規避
核心價值:通過中間件體系提升系統可觀測性,降低故障排查時間60%!
架構演進建議
-
服務拆分原則:
- 按業務域垂直拆分
- 公共能力下沉為基類
- 高頻變動邏輯獨立封裝
- 第三方服務代理隔離
-
監控體系建設:
- 服務調用鏈追蹤
- SQL執行時間監控
- 服務依賴關系圖譜
- 異常熔斷機制
通過合理的服務層設計,可使核心業務代碼量減少40%,同時提升系統可維護性。建議根據項目階段選擇適合的復用策略,初期以繼承方案為主,復雜階段采用組合模式,超大型項目可引入領域驅動設計(DDD)。歡迎在評論區留下你遇見的「服務層架構」設計經驗與挑戰,共同探討最佳實踐!