@onlineapps/service-wrapper 2.0.7 → 2.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -38
- package/docs/API_STRUCTURE_STANDARD.md +132 -0
- package/docs/ARCHITECTURE_DECISION.md +200 -0
- package/docs/CONFIGURATION_GUIDE.md +261 -0
- package/docs/FINAL_ARCHITECTURE.md +271 -0
- package/docs/HANDLER_VS_HTTP_COMPARISON.md +269 -0
- package/docs/INSTALLATION_GUIDE.md +353 -0
- package/docs/OPERATIONS_SCHEMA.md +405 -0
- package/docs/SERVICE_TESTING_STANDARD.md +389 -0
- package/docs/WRAPPER_ARCHITECTURE.md +218 -0
- package/onlineapps-service-wrapper-2.0.8.tgz +0 -0
- package/package.json +4 -3
- package/src/ServiceWrapper.js +433 -270
- package/test/monitoring-integration.test.js +150 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
# Service Testing Standard and OpenAPI Generation
|
|
2
|
+
|
|
3
|
+
## 1. OpenAPI Schema Generation
|
|
4
|
+
|
|
5
|
+
### Step 1: Analyze Your Service Endpoints
|
|
6
|
+
|
|
7
|
+
List all your service endpoints and their:
|
|
8
|
+
- HTTP methods (GET, POST, PUT, DELETE)
|
|
9
|
+
- Request parameters (query, path, body)
|
|
10
|
+
- Response structures
|
|
11
|
+
- Error responses
|
|
12
|
+
|
|
13
|
+
### Step 2: Create openapi.yaml
|
|
14
|
+
|
|
15
|
+
Create `openapi.yaml` in your service root directory with this structure:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
openapi: 3.0.0
|
|
19
|
+
info:
|
|
20
|
+
title: # Your service name from package.json
|
|
21
|
+
version: # Your service version from package.json
|
|
22
|
+
description: # Brief description of what your service does
|
|
23
|
+
|
|
24
|
+
servers:
|
|
25
|
+
- url: http://localhost:{your-port}
|
|
26
|
+
description: Local development
|
|
27
|
+
- url: http://{container-name}:3000
|
|
28
|
+
description: Docker container
|
|
29
|
+
|
|
30
|
+
paths:
|
|
31
|
+
# Document each endpoint
|
|
32
|
+
/your-endpoint:
|
|
33
|
+
get/post/put/delete:
|
|
34
|
+
summary: # One line description
|
|
35
|
+
operationId: # Unique identifier (e.g., getUsers, createOrder)
|
|
36
|
+
parameters: # If applicable
|
|
37
|
+
requestBody: # If applicable
|
|
38
|
+
responses:
|
|
39
|
+
'200':
|
|
40
|
+
description: # Success response
|
|
41
|
+
content:
|
|
42
|
+
application/json:
|
|
43
|
+
schema:
|
|
44
|
+
# Define or reference schema
|
|
45
|
+
'400':
|
|
46
|
+
# Bad request
|
|
47
|
+
'500':
|
|
48
|
+
# Server error
|
|
49
|
+
|
|
50
|
+
components:
|
|
51
|
+
schemas:
|
|
52
|
+
# Define reusable schemas here
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Step 3: Generate Schema from Code (Alternative)
|
|
56
|
+
|
|
57
|
+
If you have many endpoints, generate initial schema:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
# Install openapi generator
|
|
61
|
+
npm install --save-dev @apidevtools/swagger-cli
|
|
62
|
+
|
|
63
|
+
# For Express apps, use express-openapi-generator
|
|
64
|
+
npm install --save-dev express-openapi-generator
|
|
65
|
+
|
|
66
|
+
# Add generation script to package.json
|
|
67
|
+
"scripts": {
|
|
68
|
+
"generate:openapi": "node scripts/generate-openapi.js"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Create `scripts/generate-openapi.js`:
|
|
73
|
+
```javascript
|
|
74
|
+
const app = require('../src/app');
|
|
75
|
+
const generator = require('express-openapi-generator');
|
|
76
|
+
|
|
77
|
+
generator(app, {
|
|
78
|
+
outputFile: './openapi.yaml',
|
|
79
|
+
info: {
|
|
80
|
+
title: process.env.SERVICE_NAME,
|
|
81
|
+
version: require('../package.json').version
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Step 4: Validate Your Schema
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Install validator
|
|
90
|
+
npm install --save-dev @apidevtools/swagger-parser
|
|
91
|
+
|
|
92
|
+
# Add validation script
|
|
93
|
+
"scripts": {
|
|
94
|
+
"validate:openapi": "swagger-cli validate openapi.yaml"
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## 2. Test Structure
|
|
99
|
+
|
|
100
|
+
### Directory Structure
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
/your-service
|
|
104
|
+
/test
|
|
105
|
+
/unit # Unit tests for individual functions
|
|
106
|
+
/integration # Integration tests with mocked dependencies
|
|
107
|
+
/api # API endpoint tests
|
|
108
|
+
/compliance # OpenAPI compliance tests
|
|
109
|
+
test.config.js # Shared test configuration
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Test Configuration
|
|
113
|
+
|
|
114
|
+
Create `test/test.config.js`:
|
|
115
|
+
```javascript
|
|
116
|
+
module.exports = {
|
|
117
|
+
// Service configuration for tests
|
|
118
|
+
service: {
|
|
119
|
+
name: process.env.SERVICE_NAME || 'your-service',
|
|
120
|
+
port: process.env.PORT || 3000,
|
|
121
|
+
baseUrl: `http://localhost:${process.env.PORT || 3000}`
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
// Timeouts
|
|
125
|
+
timeouts: {
|
|
126
|
+
unit: 5000,
|
|
127
|
+
integration: 10000,
|
|
128
|
+
api: 15000
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
// Mock data
|
|
132
|
+
mocks: {
|
|
133
|
+
validRequest: { /* valid request data */ },
|
|
134
|
+
invalidRequest: { /* invalid request data */ }
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## 3. Test Implementation
|
|
140
|
+
|
|
141
|
+
### Unit Tests (`test/unit/`)
|
|
142
|
+
|
|
143
|
+
Test individual functions without external dependencies:
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
describe('Business Logic', () => {
|
|
147
|
+
test('should process valid input', () => {
|
|
148
|
+
const result = processFunction(validInput);
|
|
149
|
+
expect(result).toBeDefined();
|
|
150
|
+
expect(result.status).toBe('success');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
test('should handle invalid input', () => {
|
|
154
|
+
expect(() => processFunction(invalidInput)).toThrow();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### API Tests (`test/api/`)
|
|
160
|
+
|
|
161
|
+
Test actual HTTP endpoints:
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
const request = require('supertest');
|
|
165
|
+
const app = require('../../src/app');
|
|
166
|
+
|
|
167
|
+
describe('API Endpoints', () => {
|
|
168
|
+
describe('GET /your-endpoint', () => {
|
|
169
|
+
test('should return 200 with valid response', async () => {
|
|
170
|
+
const response = await request(app)
|
|
171
|
+
.get('/your-endpoint')
|
|
172
|
+
.expect(200);
|
|
173
|
+
|
|
174
|
+
expect(response.body).toHaveProperty('expectedField');
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('should return 400 for invalid request', async () => {
|
|
178
|
+
const response = await request(app)
|
|
179
|
+
.get('/your-endpoint?invalid=true')
|
|
180
|
+
.expect(400);
|
|
181
|
+
|
|
182
|
+
expect(response.body).toHaveProperty('error');
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Compliance Tests (`test/compliance/`)
|
|
189
|
+
|
|
190
|
+
Test OpenAPI compliance:
|
|
191
|
+
|
|
192
|
+
```javascript
|
|
193
|
+
const SwaggerParser = require('@apidevtools/swagger-parser');
|
|
194
|
+
const request = require('supertest');
|
|
195
|
+
const app = require('../../src/app');
|
|
196
|
+
const openApiSchema = require('../../openapi.yaml');
|
|
197
|
+
|
|
198
|
+
describe('OpenAPI Compliance', () => {
|
|
199
|
+
let api;
|
|
200
|
+
|
|
201
|
+
beforeAll(async () => {
|
|
202
|
+
// Validate and parse schema
|
|
203
|
+
api = await SwaggerParser.validate(openApiSchema);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
test('schema should be valid OpenAPI 3.0', () => {
|
|
207
|
+
expect(api.openapi).toBe('3.0.0');
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test('all endpoints should match schema', async () => {
|
|
211
|
+
for (const [path, methods] of Object.entries(api.paths)) {
|
|
212
|
+
for (const [method, spec] of Object.entries(methods)) {
|
|
213
|
+
if (['get', 'post', 'put', 'delete'].includes(method)) {
|
|
214
|
+
const response = await request(app)[method](path);
|
|
215
|
+
// Validate response matches schema
|
|
216
|
+
expect(response.status).toBeDefined();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## 4. NPM Scripts
|
|
225
|
+
|
|
226
|
+
Add to `package.json`:
|
|
227
|
+
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"scripts": {
|
|
231
|
+
"test": "npm run test:unit && npm run test:api && npm run test:compliance",
|
|
232
|
+
"test:unit": "jest test/unit",
|
|
233
|
+
"test:integration": "jest test/integration",
|
|
234
|
+
"test:api": "jest test/api",
|
|
235
|
+
"test:compliance": "jest test/compliance",
|
|
236
|
+
"test:coverage": "jest --coverage",
|
|
237
|
+
"validate:openapi": "swagger-cli validate openapi.yaml",
|
|
238
|
+
"generate:openapi": "node scripts/generate-openapi.js",
|
|
239
|
+
"serve:docs": "swagger-ui-express openapi.yaml"
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Coverage Requirements
|
|
245
|
+
|
|
246
|
+
**Minimum recommended coverage: 80%**
|
|
247
|
+
|
|
248
|
+
Configure Jest coverage thresholds in `package.json`:
|
|
249
|
+
|
|
250
|
+
```json
|
|
251
|
+
{
|
|
252
|
+
"jest": {
|
|
253
|
+
"coverageThreshold": {
|
|
254
|
+
"global": {
|
|
255
|
+
"branches": 80,
|
|
256
|
+
"functions": 80,
|
|
257
|
+
"lines": 80,
|
|
258
|
+
"statements": 80
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
"collectCoverageFrom": [
|
|
262
|
+
"src/**/*.js",
|
|
263
|
+
"!src/**/*.test.js",
|
|
264
|
+
"!src/**/*.spec.js"
|
|
265
|
+
]
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
## 5. Service Wrapper Integration
|
|
271
|
+
|
|
272
|
+
### Endpoint for OpenAPI Schema
|
|
273
|
+
|
|
274
|
+
Your service must expose its schema:
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
// In your app.js or routes
|
|
278
|
+
const fs = require('fs');
|
|
279
|
+
const yaml = require('js-yaml');
|
|
280
|
+
|
|
281
|
+
app.get('/openapi', (req, res) => {
|
|
282
|
+
try {
|
|
283
|
+
const doc = yaml.load(fs.readFileSync('./openapi.yaml', 'utf8'));
|
|
284
|
+
res.json(doc);
|
|
285
|
+
} catch (e) {
|
|
286
|
+
res.status(500).json({ error: 'Schema not available' });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Validation Middleware
|
|
292
|
+
|
|
293
|
+
Use Service Wrapper's validation:
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
const { validateRequest } = require('@onlineapps/service-wrapper/middleware');
|
|
297
|
+
const openApiSchema = require('./openapi.yaml');
|
|
298
|
+
|
|
299
|
+
// Apply validation to routes
|
|
300
|
+
app.post('/your-endpoint',
|
|
301
|
+
validateRequest(openApiSchema, '/your-endpoint', 'post'),
|
|
302
|
+
(req, res) => {
|
|
303
|
+
// Request is already validated
|
|
304
|
+
}
|
|
305
|
+
);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
## 6. Continuous Validation
|
|
309
|
+
|
|
310
|
+
### Pre-commit Hook
|
|
311
|
+
|
|
312
|
+
`.husky/pre-commit`:
|
|
313
|
+
```bash
|
|
314
|
+
#!/bin/sh
|
|
315
|
+
npm run validate:openapi
|
|
316
|
+
npm run test:compliance
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
### CI/CD Pipeline
|
|
320
|
+
|
|
321
|
+
```yaml
|
|
322
|
+
# .github/workflows/test.yml or gitlab-ci.yml
|
|
323
|
+
test:
|
|
324
|
+
script:
|
|
325
|
+
- npm install
|
|
326
|
+
- npm run validate:openapi
|
|
327
|
+
- npm run test
|
|
328
|
+
- npm run test:coverage
|
|
329
|
+
coverage: '/Lines\s*:\s*(\d+\.\d+)%/'
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## 7. Validation Checklist
|
|
333
|
+
|
|
334
|
+
Before deployment, ensure:
|
|
335
|
+
|
|
336
|
+
- [ ] OpenAPI schema exists at `/openapi.yaml`
|
|
337
|
+
- [ ] Schema validates with `swagger-cli validate`
|
|
338
|
+
- [ ] All endpoints documented in schema
|
|
339
|
+
- [ ] Request/response schemas defined
|
|
340
|
+
- [ ] Error responses documented
|
|
341
|
+
- [ ] Schema version matches package.json
|
|
342
|
+
- [ ] `/openapi` endpoint returns schema
|
|
343
|
+
- [ ] Unit tests pass (>80% coverage)
|
|
344
|
+
- [ ] API tests pass
|
|
345
|
+
- [ ] Compliance tests pass
|
|
346
|
+
- [ ] Service Wrapper can load schema
|
|
347
|
+
- [ ] Validation middleware works
|
|
348
|
+
|
|
349
|
+
## 8. Common Issues and Solutions
|
|
350
|
+
|
|
351
|
+
### Issue: Schema doesn't match implementation
|
|
352
|
+
**Solution**: Generate schema from code first, then refine manually
|
|
353
|
+
|
|
354
|
+
### Issue: Validation too strict
|
|
355
|
+
**Solution**: Use `additionalProperties: true` in schemas during development
|
|
356
|
+
|
|
357
|
+
### Issue: Complex nested objects
|
|
358
|
+
**Solution**: Break into components and use `$ref`
|
|
359
|
+
|
|
360
|
+
### Issue: Different environments need different configs
|
|
361
|
+
**Solution**: Use environment variables in schema:
|
|
362
|
+
```yaml
|
|
363
|
+
servers:
|
|
364
|
+
- url: ${API_BASE_URL}
|
|
365
|
+
description: Configured base URL
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## 9. Testing with conn-e2e-testing
|
|
369
|
+
|
|
370
|
+
The connector testing framework will:
|
|
371
|
+
1. Load your OpenAPI schema
|
|
372
|
+
2. Start your service
|
|
373
|
+
3. Test each endpoint against schema
|
|
374
|
+
4. Validate requests and responses
|
|
375
|
+
5. Generate compliance report
|
|
376
|
+
|
|
377
|
+
Ensure your service is ready by:
|
|
378
|
+
1. Exposing `/openapi` endpoint
|
|
379
|
+
2. Having all endpoints documented
|
|
380
|
+
3. Returning proper HTTP status codes
|
|
381
|
+
4. Using consistent error format
|
|
382
|
+
|
|
383
|
+
## 10. Resources
|
|
384
|
+
|
|
385
|
+
- [OpenAPI 3.0 Specification](https://swagger.io/specification/)
|
|
386
|
+
- [JSON Schema Validation](https://json-schema.org/draft/2019-09/json-schema-validation.html)
|
|
387
|
+
- [swagger-cli Documentation](https://apitools.dev/swagger-cli/)
|
|
388
|
+
- Service Wrapper documentation: `/shared/connector/service-wrapper/README.md`
|
|
389
|
+
- Testing examples: `/shared/connector/conn-e2e-testing/examples/`
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Service Wrapper Architecture
|
|
2
|
+
|
|
3
|
+
> **NOTE: This document describes initial architecture concepts. For the production-ready decision, see [FINAL_ARCHITECTURE.md](./FINAL_ARCHITECTURE.md)**
|
|
4
|
+
|
|
5
|
+
## Definition
|
|
6
|
+
|
|
7
|
+
Service Wrapper is a **collection of connectors** that provides all components necessary for a business service to function within the system.
|
|
8
|
+
|
|
9
|
+
## Core Principles
|
|
10
|
+
|
|
11
|
+
1. **Component Collection** - Wrapper is NOT a framework but a set of reusable components
|
|
12
|
+
2. **Central Management** - All services use the same wrapper version for consistency
|
|
13
|
+
3. **Version Independence** - Each service can pin its wrapper version as needed
|
|
14
|
+
4. **Duplicate by Design** - Each service has its own wrapper instance (not shared)
|
|
15
|
+
|
|
16
|
+
## Architecture Pattern: Enhanced Service
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
// Business service enhanced with wrapper components
|
|
20
|
+
const express = require('express');
|
|
21
|
+
const { MQConnector, MonitoringConnector, HealthConnector } = require('@onlineapps/service-wrapper');
|
|
22
|
+
|
|
23
|
+
class EnhancedService {
|
|
24
|
+
constructor() {
|
|
25
|
+
// Core business service
|
|
26
|
+
this.app = express();
|
|
27
|
+
this.setupBusinessRoutes();
|
|
28
|
+
|
|
29
|
+
// Wrapper components
|
|
30
|
+
this.mq = new MQConnector(this);
|
|
31
|
+
this.monitoring = new MonitoringConnector(this);
|
|
32
|
+
this.health = new HealthConnector(this);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async start() {
|
|
36
|
+
// 1. Start HTTP server
|
|
37
|
+
await this.app.listen(this.config.port);
|
|
38
|
+
|
|
39
|
+
// 2. Connect to MQ
|
|
40
|
+
await this.mq.connect();
|
|
41
|
+
await this.mq.consume('workflow.init', this.handleWorkflow.bind(this));
|
|
42
|
+
await this.mq.consume(`${this.config.name}.workflow`, this.handleServiceMessage.bind(this));
|
|
43
|
+
|
|
44
|
+
// 3. Start monitoring
|
|
45
|
+
await this.monitoring.start();
|
|
46
|
+
|
|
47
|
+
// 4. Register health checks
|
|
48
|
+
this.health.register('/health', this.checkHealth.bind(this));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
handleWorkflow(message) {
|
|
52
|
+
// Wrapper handles workflow routing
|
|
53
|
+
const operation = this.config.operations[message.step.operation];
|
|
54
|
+
|
|
55
|
+
// Call own HTTP endpoint
|
|
56
|
+
return this.callHttp(operation.endpoint, message.input);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
callHttp(endpoint, data) {
|
|
60
|
+
// Internal HTTP call to business logic
|
|
61
|
+
return fetch(`http://localhost:${this.config.port}${endpoint}`, {
|
|
62
|
+
method: 'POST',
|
|
63
|
+
body: JSON.stringify(data)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Component Responsibilities
|
|
70
|
+
|
|
71
|
+
### MQ Connector
|
|
72
|
+
- Listen on `workflow.init` queue
|
|
73
|
+
- Listen on service-specific queues
|
|
74
|
+
- Route messages between services
|
|
75
|
+
- Handle dead letter queues
|
|
76
|
+
|
|
77
|
+
### Monitoring Connector
|
|
78
|
+
- Collect metrics
|
|
79
|
+
- Export to monitoring system
|
|
80
|
+
- Track request/response times
|
|
81
|
+
- Monitor queue depth
|
|
82
|
+
|
|
83
|
+
### Health Connector
|
|
84
|
+
- Provide `/health` endpoint
|
|
85
|
+
- Check service dependencies
|
|
86
|
+
- Report readiness status
|
|
87
|
+
- Aggregate component health
|
|
88
|
+
|
|
89
|
+
### Registry Connector
|
|
90
|
+
- Register service on startup
|
|
91
|
+
- Send heartbeat
|
|
92
|
+
- Update capabilities
|
|
93
|
+
- Handle deregistration
|
|
94
|
+
|
|
95
|
+
## Single Process Architecture
|
|
96
|
+
|
|
97
|
+
**Decision:** Use single process with embedded wrapper components.
|
|
98
|
+
|
|
99
|
+
### Rationale:
|
|
100
|
+
1. **Simplicity** - One deployment unit
|
|
101
|
+
2. **Efficiency** - No IPC overhead
|
|
102
|
+
3. **Atomicity** - Components share state
|
|
103
|
+
4. **Debugging** - Single log stream
|
|
104
|
+
|
|
105
|
+
### Implementation:
|
|
106
|
+
```javascript
|
|
107
|
+
// service/index.js
|
|
108
|
+
const ServiceWrapper = require('@onlineapps/service-wrapper');
|
|
109
|
+
const app = require('./src/app'); // Express app
|
|
110
|
+
const config = require('./conn-config/config.json');
|
|
111
|
+
const operations = require('./conn-config/operations.json');
|
|
112
|
+
|
|
113
|
+
// Single process startup
|
|
114
|
+
async function start() {
|
|
115
|
+
// Business service
|
|
116
|
+
const server = app.listen(config.port);
|
|
117
|
+
|
|
118
|
+
// Wrapper components
|
|
119
|
+
const wrapper = new ServiceWrapper({
|
|
120
|
+
app,
|
|
121
|
+
server,
|
|
122
|
+
config,
|
|
123
|
+
operations
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
await wrapper.initialize();
|
|
127
|
+
console.log(`Service ${config.name} running with wrapper components`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
start().catch(console.error);
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Configuration Pattern
|
|
134
|
+
|
|
135
|
+
### operations.json
|
|
136
|
+
```json
|
|
137
|
+
{
|
|
138
|
+
"operations": {
|
|
139
|
+
"operation-name": {
|
|
140
|
+
"endpoint": "/api/operation",
|
|
141
|
+
"method": "POST",
|
|
142
|
+
"input": { "schema": "..." },
|
|
143
|
+
"output": { "schema": "..." }
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### config.json
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"service": {
|
|
153
|
+
"name": "my-service",
|
|
154
|
+
"port": 3000
|
|
155
|
+
},
|
|
156
|
+
"wrapper": {
|
|
157
|
+
"mq": {
|
|
158
|
+
"url": "${RABBITMQ_URL}"
|
|
159
|
+
},
|
|
160
|
+
"monitoring": {
|
|
161
|
+
"enabled": true
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Health Check Strategy
|
|
168
|
+
|
|
169
|
+
Health checks are **provided by wrapper** but **implemented by service**:
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
// Wrapper provides endpoint
|
|
173
|
+
app.get('/health', healthConnector.handler);
|
|
174
|
+
|
|
175
|
+
// Service implements check
|
|
176
|
+
class MyService {
|
|
177
|
+
checkHealth() {
|
|
178
|
+
return {
|
|
179
|
+
status: this.isHealthy() ? 'healthy' : 'unhealthy',
|
|
180
|
+
database: this.db.isConnected(),
|
|
181
|
+
dependencies: this.checkDependencies()
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Migration Path
|
|
188
|
+
|
|
189
|
+
### From Standalone Service:
|
|
190
|
+
1. Add `@onlineapps/service-wrapper` dependency
|
|
191
|
+
2. Create `conn-config/operations.json`
|
|
192
|
+
3. Wrap service startup with ServiceWrapper
|
|
193
|
+
4. Remove duplicate infrastructure code
|
|
194
|
+
|
|
195
|
+
### From Two-Process Architecture:
|
|
196
|
+
1. Merge wrapper.js into service index.js
|
|
197
|
+
2. Remove separate wrapper process
|
|
198
|
+
3. Consolidate configuration
|
|
199
|
+
4. Single deployment unit
|
|
200
|
+
|
|
201
|
+
## Benefits
|
|
202
|
+
|
|
203
|
+
1. **No Duplication** - Common components in wrapper
|
|
204
|
+
2. **Central Updates** - Update wrapper version for all services
|
|
205
|
+
3. **Consistent Behavior** - All services work the same way
|
|
206
|
+
4. **Simple Deployment** - One process per service
|
|
207
|
+
5. **Clear Separation** - Business logic vs infrastructure
|
|
208
|
+
|
|
209
|
+
## Drawbacks
|
|
210
|
+
|
|
211
|
+
1. **Tight Coupling** - Service depends on wrapper version
|
|
212
|
+
2. **Restart Required** - Any change restarts everything
|
|
213
|
+
3. **Mixed Dependencies** - Business and infrastructure together
|
|
214
|
+
4. **Testing Complexity** - Must mock wrapper components
|
|
215
|
+
|
|
216
|
+
## Conclusion
|
|
217
|
+
|
|
218
|
+
Service Wrapper as a **component collection** in a **single process** provides the best balance of simplicity, efficiency, and maintainability for our microservices architecture.
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@onlineapps/service-wrapper",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.9",
|
|
4
4
|
"description": "Thin orchestration layer for microservices - delegates all infrastructure concerns to specialized connectors",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -25,13 +25,14 @@
|
|
|
25
25
|
"license": "MIT",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@onlineapps/conn-base-cache": "^1.0.0",
|
|
28
|
-
"@onlineapps/conn-base-
|
|
28
|
+
"@onlineapps/conn-base-monitoring": "^1.0.0",
|
|
29
29
|
"@onlineapps/conn-infra-error-handler": "^1.0.0",
|
|
30
30
|
"@onlineapps/conn-infra-mq": "^1.1.0",
|
|
31
31
|
"@onlineapps/conn-orch-api-mapper": "^1.0.0",
|
|
32
32
|
"@onlineapps/conn-orch-cookbook": "^2.0.0",
|
|
33
33
|
"@onlineapps/conn-orch-orchestrator": "^1.0.1",
|
|
34
|
-
"@onlineapps/conn-orch-registry": "^1.1.4"
|
|
34
|
+
"@onlineapps/conn-orch-registry": "^1.1.4",
|
|
35
|
+
"@onlineapps/monitoring-core": "^1.0.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"express": "^5.1.0",
|