Code organization and architecture evolution strategy
Code Organization and Architecture Evolution Strategies
Koa2, as a lightweight web framework in the Node.js ecosystem, provides a flexible foundation for application architecture with its onion-ring model and asynchronous middleware mechanism. However, as business complexity increases, organizing code and planning architecture evolution paths become critical. Below are specific practical solutions from the dimensions of directory structure design, middleware layering, and plugin-based extensibility.
Basic Directory Structure Design
A typical Koa2 project initially adopts a functional dimension division:
/src
/controllers
/models
/routes
/middlewares
app.js
When the number of routes exceeds 20, it is recommended to switch to a business module division:
/src
/modules
/user
user.controller.js
user.model.js
user.router.js
/order
order.controller.js
order.model.js
order.router.js
/core
/middlewares
/services
app.js
Automate route registration through dynamic loading:
// app.js
const fs = require('fs')
const path = require('path')
fs.readdirSync('./src/modules').forEach(module => {
const router = require(`./modules/${module}/${module}.router`)
app.use(router.routes())
})
Middleware Layering Strategy
Infrastructure Layer Middleware
Handles basic HTTP capabilities, recommended execution order:
app.use(require('koa-helmet')()) // Security headers
app.use(require('koa-compress')()) // Compression
app.use(require('koa-bodyparser')()) // Body parsing
Business Logic Layer Middleware
Implements specific business rules, typical pattern:
// Order status validation middleware
async function orderStatusValidator(ctx, next) {
const order = await OrderService.getById(ctx.params.id)
if (order.status !== 'paid') {
ctx.throw(403, 'Order not paid')
}
ctx.state.order = order // Mount to context
await next()
}
// Usage in routes
router.post('/refund',
orderStatusValidator,
async ctx => {
await RefundService.create(ctx.state.order)
}
)
Plugin-Based Architecture Evolution
When multi-tenancy support is needed, transform via a plugin mechanism:
// plugins/tenant/index.js
module.exports = function tenantPlugin(app) {
app.context.getTenantDB = function() {
return this.headers['x-tenant-id']
? getTenantConnection(this.headers['x-tenant-id'])
: defaultDB
}
app.use(async (ctx, next) => {
ctx.db = ctx.getTenantDB()
await next()
})
}
// app.js
const tenant = require('./plugins/tenant')
tenant(app)
Advanced Configuration Management
Evolution from basic to dynamic configuration:
// config/default.js
module.exports = {
db: {
host: process.env.DB_HOST || 'localhost',
pool: {
max: process.env.NODE_ENV === 'production' ? 20 : 5
}
}
}
// Dynamic configuration loader
class ConfigLoader {
constructor() {
this._config = require('./config/default')
this.watchFile()
}
watchFile() {
fs.watch('./config', (event, filename) => {
if (filename.endsWith('.js')) {
this._config = require('./config/' + filename)
}
})
}
get config() {
return this._config
}
}
Exception Handling System Construction
Example of a tiered error handling mechanism:
// core/error.js
class BusinessError extends Error {
constructor(code, message) {
super(message)
this.code = code
}
}
// Middleware handling
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
if (err instanceof BusinessError) {
ctx.status = 400
ctx.body = { code: err.code, msg: err.message }
} else {
ctx.status = 500
ctx.body = { code: 'SERVER_ERROR', msg: 'System error' }
ctx.app.emit('error', err, ctx) // Trigger logging
}
}
})
// Usage in business logic
router.post('/create', async ctx => {
if (!ctx.request.body.name) {
throw new BusinessError('INVALID_PARAM', 'Name cannot be empty')
}
})
Performance Optimization Architecture Adjustments
Architecture transformation for high-concurrency scenarios:
// Introduce route-level caching
const LRU = require('lru-cache')
const productCache = new LRU({
max: 1000,
maxAge: 1000 * 60 * 5 // 5 minutes
})
router.get('/products/:id',
async (ctx, next) => {
const cached = productCache.get(ctx.params.id)
if (cached) {
ctx.body = cached
return
}
await next()
},
async ctx => {
const product = await ProductService.getDetail(ctx.params.id)
productCache.set(ctx.params.id, product)
ctx.body = product
}
)
// Database connection pool optimization
const knex = require('knex')({
client: 'mysql2',
connection: {
pool: {
afterCreate: (conn, done) => {
conn.query('SET SESSION wait_timeout = 28800', done)
}
}
}
})
Microservices Evolution Path
Gradual transition plan for splitting:
// gateway/proxy.js
const httpProxy = require('http-proxy-middleware')
module.exports = function(serviceName) {
return async (ctx, next) => {
if (ctx.path.startsWith(`/api/${serviceName}`)) {
return httpProxy({
target: `http://${serviceName}-service:3000`,
pathRewrite: { [`^/api/${serviceName}`]: '' },
changeOrigin: true
})(ctx.req, ctx.res, next)
}
await next()
}
}
// app.js
app.use(require('./gateway/proxy')('user'))
app.use(require('./gateway/proxy')('order'))
Introduction of Type System
Hybrid solution for gradually adopting TypeScript:
// ts-check.js
const { execSync } = require('child_process')
module.exports = function typesafeMiddleware() {
return async (ctx, next) => {
try {
execSync('npm run type-check', { stdio: 'pipe' })
await next()
} catch (e) {
ctx.status = 500
ctx.body = { error: 'Type check failed', details: e.stdout.toString() }
}
}
}
// Enable type checking in development environment
if (process.env.NODE_ENV === 'development') {
app.use(require('./ts-check')())
}
Monitoring System Architecture Integration
Example of a production monitoring solution:
// core/monitor.js
const { NodeSDK } = require('@opentelemetry/sdk-node')
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node')
module.exports = function initTelemetry() {
const sdk = new NodeSDK({
traceExporter: new ConsoleSpanExporter(),
instrumentations: [getNodeAutoInstrumentations()]
})
sdk.start()
process.on('SIGTERM', () => {
sdk.shutdown()
.then(() => console.log('Tracing terminated'))
.catch(err => console.error('Error terminating tracing', err))
})
}
// Import at the top of app.js
require('./core/monitor')()
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:领域驱动设计初步应用
下一篇:基准测试与性能分析工具