在使用 Node.js 构建 BFF(Backend for Frontend)层架构时,核心思想是为前端应用(如 Web、移动端或桌面应用)提供定制化的 API 接口,将后端复杂的服务逻辑抽象化、聚合化,从而优化前端开发体验并提升系统整体性能。
以下是基于 Node.js 构建 BFF 层架构的一些关键点和实践方法:
职责:
位置:
Node.js 框架:
其他工具:
axios
或 node-fetch
调用后端服务。node-cache
)优化性能。winston
或 pino
等日志库,以及 Prometheus
等监控工具。plaintext
Frontend (Web/Mobile)
↓
BFF Layer (Node.js)
├── Router (定义 API 接口)
├── Service (业务逻辑)
├── Aggregator (数据聚合)
├── Adapter (前端适配)
└── External Services (调用后端微服务/数据库)
假设有一个电商应用,前端需要展示商品详情,包括商品信息、库存、用户评价等。
BFF 层可以调用多个微服务:
BFF 层将这些数据整合后返回给前端。
示例代码:
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/product/:id', async (req, res) => {
const productId = req.params.id;
try {
// 调用多个微服务
const [product, inventory, reviews] = await Promise.all([
axios.get(`https://product-service/api/products/${productId}`),
axios.get(`https://inventory-service/api/inventory/${productId}`),
axios.get(`https://review-service/api/reviews/${productId}`),
]);
// 聚合数据
const result = {
product: product.data,
inventory: inventory.data,
reviews: reviews.data,
};
res.json(result);
} catch (error) {
res.status(500).send('Error fetching product data');
}
});
app.listen(3000, () => {
console.log('BFF server running on port 3000');
});
示例代码:
const jwt = require('jsonwebtoken');
function authenticateToken(req, res, next) {
const authHeader = req.headers['authorization'];
const token = authHeader && authHeader.split(' ')[1];
if (token == null) return res.sendStatus(401);
jwt.verify(token, 'your-secret-key', (err, user) => {
if (err) return res.sendStatus(403);
req.user = user;
next();
});
}
app.get('/secure-data', authenticateToken, (req, res) => {
res.json({ message: 'This is secure data', user: req.user });
});
示例代码:
const redis = require('redis');
const client = redis.createClient();
app.get('/cached-data/:id', async (req, res) => {
const id = req.params.id;
const cacheKey = `data:${id}`;
client.get(cacheKey, async (err, data) => {
if (data) {
res.json(JSON.parse(data));
} else {
const result = await axios.get(`https://some-service/api/data/${id}`);
client.setex(cacheKey, 3600, JSON.stringify(result.data)); // 缓存 1 小时
res.json(result.data);
}
});
});
在基于 ECharts 图表的数据展示场景中,BFF(Backend for Frontend)层可以承担数据聚合和格式化的任务,从而让前端专注于图表的渲染逻辑,同时减少前端与多个后端服务的交互复杂度。
假设需要展示一个包含以下信息的 ECharts 图表:
sales-service
获取)。user-service
获取)。BFF 层的目标是:
ECharts 图表通常需要以下数据结构:
示例 ECharts 配置:
option = {
xAxis: {
type: 'category',
data: ['2023-10-01', '2023-10-02', '2023-10-03'], // X 轴数据
},
yAxis: {
type: 'value',
},
series: [
{
name: '销售额',
type: 'line',
data: [120, 200, 150], // Y 轴数据
},
{
name: '用户数',
type: 'line',
data: [50, 80, 70],
},
],
};
前端通过查询参数传递时间范围,例如:
GET /chart-data?startDate=2023-10-01&endDate=2023-10-07
BFF 层调用多个服务获取数据,例如:
sales-service
:返回指定时间范围内的销售数据。user-service
:返回指定时间范围内的用户增长数据。示例代码:
const express = require('express');
const axios = require('axios');
const app = express();
app.get('/chart-data', async (req, res) => {
const { startDate, endDate } = req.query;
try {
// 调用销售服务
const salesResponse = await axios.get(`https://sales-service/api/sales`, {
params: { startDate, endDate },
});
const salesData = salesResponse.data; // 假设返回 [{ date: '2023-10-01', amount: 120 }, ...]
// 调用用户服务
const userResponse = await axios.get(`https://user-service/api/users`, {
params: { startDate, endDate },
});
const userData = userResponse.data; // 假设返回 [{ date: '2023-10-01', count: 50 }, ...]
// 数据聚合与格式化
const dateSet = new Set();
const salesMap = {};
const userMap = {};
// 收集所有日期
salesData.forEach(item => dateSet.add(item.date));
userData.forEach(item => dateSet.add(item.date));
// 构建日期数组
const xAxisData = Array.from(dateSet).sort();
// 填充销售数据
salesData.forEach(item => {
salesMap[item.date] = item.amount;
});
// 填充用户数据
userData.forEach(item => {
userMap[item.date] = item.count;
});
// 构建 Y 轴数据
const salesSeries = xAxisData.map(date => salesMap[date] || 0);
const userSeries = xAxisData.map(date => userMap[date] || 0);
// 返回 ECharts 所需格式
res.json({
xAxis: xAxisData,
series: [
{ name: '销售额', type: 'line', data: salesSeries },
{ name: '用户数', type: 'line', data: userSeries },
],
});
} catch (error) {
res.status(500).send('Error fetching chart data');
}
});
app.listen(3000, () => {
console.log('BFF server running on port 3000');
});
前端只需要调用 BFF 层提供的接口,并直接将返回的数据传递给 ECharts:
fetch('/chart-data?startDate=2023-10-01&endDate=2023-10-07')
.then(response => response.json())
.then(data => {
const option = {
xAxis: {
type: 'category',
data: data.xAxis,
},
yAxis: {
type: 'value',
},
series: data.series,
};
const chart = echarts.init(document.getElementById('main'));
chart.setOption(option);
});
通过以上方法,你可以使用 Node.js 构建一个高效、灵活的 BFF 层架构,为前端提供更好的开发体验,同时优化后端服务的调用效率。