@friggframework/devtools 2.0.0--canary.474.a74bb09.0 → 2.0.0--canary.474.295ae1f.0

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.
@@ -1,703 +0,0 @@
1
- # Multi-Cloud Architecture - Discovery, Doctor & Repair
2
-
3
- ## Overview
4
-
5
- This document describes the architecture for multi-cloud support in Frigg's infrastructure tooling, with a focus on the discovery, health checking (doctor), and repair capabilities.
6
-
7
- **Key Principle**: Use Domain-Driven Design (DDD) and Hexagonal Architecture (Ports & Adapters) to support AWS today while making it obvious where to extend for GCP, Azure, Cloudflare, and non-serverless (Docker) deployments.
8
-
9
- ---
10
-
11
- ## Architecture Layers
12
-
13
- ```
14
- ┌──────────────────────────────────────────────────────────────┐
15
- │ CLI LAYER │
16
- │ frigg doctor | frigg repair | frigg deploy │
17
- └────────────────────────┬─────────────────────────────────────┘
18
-
19
- ┌────────────────────────▼─────────────────────────────────────┐
20
- │ APPLICATION LAYER (Use Cases) │
21
- │ Orchestrates business logic - provider agnostic │
22
- │ │
23
- │ • RunHealthCheckUseCase │
24
- │ • RepairStackViaImportUseCase │
25
- │ • ReconcilePropertyMismatchesUseCase │
26
- │ • DiscoverInfrastructureUseCase │
27
- └────────────────────────┬─────────────────────────────────────┘
28
-
29
- │ Uses Ports (Interfaces)
30
-
31
- ┌────────────────────────▼─────────────────────────────────────┐
32
- │ PORT INTERFACES (Boundaries) │
33
- │ Define contracts - implemented by adapters │
34
- │ │
35
- │ • IStackRepository - Stack CRUD operations │
36
- │ • IResourceDetector - Cloud resource queries │
37
- │ • IDriftDetector - Compare desired vs actual │
38
- │ • IResourceImporter - Import existing resources │
39
- │ • IPropertyReconciler - Fix property mismatches │
40
- └────────────────────────┬─────────────────────────────────────┘
41
-
42
- │ Implemented by
43
-
44
- ┌────────────────────────▼─────────────────────────────────────┐
45
- │ ADAPTER LAYER (Provider-Specific) │
46
- │ │
47
- │ ┌─────────────────┐ ┌─────────────────┐ ┌──────────────┐│
48
- │ │ AWS Adapters │ │ GCP Adapters │ │ Azure ││
49
- │ │ (Today) │ │ (Future) │ │ Adapters ││
50
- │ │ │ │ │ │ (Future) ││
51
- │ │ • CloudFormation│ │ • Deployment │ │ • ARM ││
52
- │ │ • AWS SDK APIs │ │ Manager │ │ Templates ││
53
- │ │ • Resource │ │ • GCP APIs │ │ • Azure ││
54
- │ │ Importers │ │ │ │ APIs ││
55
- │ └─────────────────┘ └─────────────────┘ └──────────────┘│
56
- └──────────────────────────────────────────────────────────────┘
57
-
58
- ┌────────────────────────▼─────────────────────────────────────┐
59
- │ CLOUD PROVIDERS │
60
- │ AWS | GCP | Azure | Cloudflare │
61
- └──────────────────────────────────────────────────────────────┘
62
- ```
63
-
64
- ---
65
-
66
- ## Domain Structure
67
-
68
- ### Directory Organization
69
-
70
- ```
71
- packages/devtools/infrastructure/
72
- ├── domains/
73
- │ ├── health/ # NEW - Health checking domain
74
- │ │ ├── domain/ # Domain layer (provider-agnostic)
75
- │ │ │ ├── entities/
76
- │ │ │ │ ├── Resource.js
77
- │ │ │ │ ├── Issue.js
78
- │ │ │ │ ├── PropertyMismatch.js
79
- │ │ │ │ └── StackHealthReport.js
80
- │ │ │ ├── value-objects/
81
- │ │ │ │ ├── StackIdentifier.js
82
- │ │ │ │ ├── HealthScore.js
83
- │ │ │ │ ├── ResourceState.js # IN_STACK, ORPHANED, MISSING, DRIFTED
84
- │ │ │ │ └── PropertyMutability.js
85
- │ │ │ ├── services/
86
- │ │ │ │ ├── HealthScoreCalculator.js
87
- │ │ │ │ └── MismatchAnalyzer.js
88
- │ │ │ └── collections/
89
- │ │ │ ├── ResourceCollection.js
90
- │ │ │ └── IssueCollection.js
91
- │ │ ├── application/ # Application layer (use cases)
92
- │ │ │ ├── use-cases/
93
- │ │ │ │ ├── run-health-check-use-case.js
94
- │ │ │ │ ├── repair-via-import-use-case.js
95
- │ │ │ │ └── reconcile-properties-use-case.js
96
- │ │ │ └── ports/ # Port interfaces
97
- │ │ │ ├── IStackRepository.js
98
- │ │ │ ├── IResourceDetector.js
99
- │ │ │ ├── IDriftDetector.js
100
- │ │ │ ├── IResourceImporter.js
101
- │ │ │ └── IPropertyReconciler.js
102
- │ │ └── infrastructure/ # Infrastructure layer (adapters)
103
- │ │ ├── adapters/
104
- │ │ │ ├── aws/ # AWS implementations (TODAY)
105
- │ │ │ │ ├── AWSStackRepository.js
106
- │ │ │ │ ├── AWSResourceDetector.js
107
- │ │ │ │ ├── AWSDriftDetector.js
108
- │ │ │ │ ├── AWSResourceImporter.js
109
- │ │ │ │ └── AWSPropertyReconciler.js
110
- │ │ │ ├── gcp/ # GCP implementations (FUTURE)
111
- │ │ │ │ ├── GCPStackRepository.js
112
- │ │ │ │ └── ...
113
- │ │ │ └── azure/ # Azure implementations (FUTURE)
114
- │ │ │ ├── AzureStackRepository.js
115
- │ │ │ └── ...
116
- │ │ └── cli/
117
- │ │ ├── doctor-command.js
118
- │ │ ├── repair-command.js
119
- │ │ └── presenters/
120
- │ │ ├── health-report-presenter.js
121
- │ │ └── repair-plan-presenter.js
122
- │ │
123
- │ ├── discovery/ # REFACTORED - Cloud discovery domain
124
- │ │ ├── domain/
125
- │ │ │ ├── entities/
126
- │ │ │ │ ├── DiscoveryResult.js
127
- │ │ │ │ └── CloudResource.js
128
- │ │ │ └── value-objects/
129
- │ │ │ └── ResourceIdentifier.js
130
- │ │ ├── application/
131
- │ │ │ ├── use-cases/
132
- │ │ │ │ └── discover-infrastructure-use-case.js
133
- │ │ │ └── ports/
134
- │ │ │ ├── ICloudProvider.js # Port for cloud providers
135
- │ │ │ └── IStackProvider.js # Port for stack systems
136
- │ │ └── infrastructure/
137
- │ │ └── adapters/
138
- │ │ ├── aws/
139
- │ │ │ ├── AWSCloudProvider.js
140
- │ │ │ ├── CloudFormationStackProvider.js
141
- │ │ │ ├── EC2Discoverer.js
142
- │ │ │ ├── RDSDiscoverer.js
143
- │ │ │ └── KMSDiscoverer.js
144
- │ │ ├── gcp/
145
- │ │ │ ├── GCPCloudProvider.js
146
- │ │ │ └── DeploymentManagerStackProvider.js
147
- │ │ └── azure/
148
- │ │ ├── AzureCloudProvider.js
149
- │ │ └── ARMTemplateStackProvider.js
150
- │ │
151
- │ ├── networking/ # PROVIDER-SPECIFIC builders
152
- │ │ ├── aws/
153
- │ │ │ ├── vpc-builder.js
154
- │ │ │ └── vpc-resolver.js
155
- │ │ ├── gcp/
156
- │ │ │ ├── network-builder.js # (FUTURE)
157
- │ │ │ └── network-resolver.js
158
- │ │ └── azure/
159
- │ │ ├── vnet-builder.js # (FUTURE)
160
- │ │ └── vnet-resolver.js
161
- │ │
162
- │ ├── database/ # PROVIDER-SPECIFIC builders
163
- │ │ ├── aws/
164
- │ │ │ ├── aurora-builder.js
165
- │ │ │ ├── aurora-resolver.js
166
- │ │ │ ├── migration-builder.js
167
- │ │ │ └── migration-resolver.js
168
- │ │ ├── gcp/
169
- │ │ │ ├── cloud-sql-builder.js # (FUTURE)
170
- │ │ │ └── cloud-sql-resolver.js
171
- │ │ └── azure/
172
- │ │ ├── cosmos-db-builder.js # (FUTURE)
173
- │ │ └── cosmos-db-resolver.js
174
- │ │
175
- │ ├── security/ # PROVIDER-SPECIFIC builders
176
- │ │ ├── aws/
177
- │ │ │ ├── kms-builder.js
178
- │ │ │ └── kms-resolver.js
179
- │ │ ├── gcp/
180
- │ │ │ └── kms-builder.js # (FUTURE)
181
- │ │ └── azure/
182
- │ │ └── key-vault-builder.js # (FUTURE)
183
- │ │
184
- │ └── shared/ # Shared utilities
185
- │ ├── base-resource-resolver.js
186
- │ ├── builder-orchestrator.js
187
- │ └── resource-ownership.js
188
-
189
- └── providers/ # PROVIDER REGISTRY
190
- ├── registry.js # Maps provider name to implementations
191
- ├── aws-provider.js # AWS provider definition
192
- ├── gcp-provider.js # GCP provider definition (FUTURE)
193
- └── azure-provider.js # Azure provider definition (FUTURE)
194
- ```
195
-
196
- ---
197
-
198
- ## Port Interfaces (Contracts)
199
-
200
- Port interfaces define the contracts that provider-specific adapters must implement. These are the boundaries between the provider-agnostic domain layer and provider-specific infrastructure.
201
-
202
- **Source files**: `packages/devtools/infrastructure/domains/health/application/ports/`
203
-
204
- ### Key Ports
205
-
206
- **IStackRepository** - Stack management operations (CloudFormation, Deployment Manager, ARM)
207
- ```javascript
208
- // Example methods:
209
- async getStack(identifier)
210
- async listResources(identifier)
211
- async getOutputs(identifier)
212
- ```
213
- 📄 See: `application/ports/IStackRepository.js`
214
-
215
- **IResourceDetector** - Cloud resource discovery (AWS APIs, GCP APIs, Azure APIs)
216
- ```javascript
217
- // Example methods:
218
- async detectNetworks(region)
219
- async detectDatabases(region)
220
- async detectKeys(region)
221
- async detectResourceById(physicalId, resourceType)
222
- ```
223
- 📄 See: `application/ports/IResourceDetector.js`
224
-
225
- **IDriftDetector** - Compare desired state vs actual state
226
- ```javascript
227
- // Example methods:
228
- async detectDrift(resource, desiredProperties)
229
- async detectStackDrift(identifier)
230
- ```
231
- 📄 See: `application/ports/IDriftDetector.js`
232
-
233
- **IResourceImporter** - Import existing resources into stack
234
- ```javascript
235
- // Example methods:
236
- isImportable(resourceType)
237
- async createImportChangeSet(stackId, resources)
238
- async executeImport(changeSet)
239
- ```
240
- 📄 See: `application/ports/IResourceImporter.js`
241
-
242
- **IPropertyReconciler** - Fix property mismatches
243
- ```javascript
244
- // Example methods:
245
- async reconcile(mismatch, resource)
246
- async planReconciliation(mismatches)
247
- ```
248
- 📄 See: `application/ports/IPropertyReconciler.js`
249
-
250
- > **Note**: Full interface definitions are maintained in source files. See the actual TypeScript/JSDoc definitions for complete method signatures and documentation.
251
-
252
- ---
253
-
254
- ## AWS Adapter Implementations
255
-
256
- AWS-specific implementations of the port interfaces using AWS SDK v3.
257
-
258
- **Source files**: `packages/devtools/infrastructure/domains/health/infrastructure/adapters/aws/`
259
-
260
- ### AWSStackRepository
261
-
262
- Implements `IStackRepository` using CloudFormation API.
263
-
264
- ```javascript
265
- class AWSStackRepository extends IStackRepository {
266
- constructor({ region }) {
267
- this.client = new CloudFormationClient({ region });
268
- }
269
-
270
- async getStack(identifier) {
271
- // Uses DescribeStacksCommand
272
- // Returns Stack or null if not found
273
- }
274
-
275
- async listResources(identifier) {
276
- // Uses ListStackResourcesCommand
277
- // Maps to standard resource format
278
- }
279
- }
280
- ```
281
-
282
- 📄 See full implementation: `adapters/aws/AWSStackRepository.js`
283
-
284
- ### AWSResourceDetector
285
-
286
- Implements `IResourceDetector` using AWS service APIs (EC2, RDS, KMS, etc.).
287
-
288
- ```javascript
289
- class AWSResourceDetector extends IResourceDetector {
290
- constructor({ region }) {
291
- this.ec2 = new EC2Client({ region });
292
- this.rds = new RDSClient({ region });
293
- this.kms = new KMSClient({ region });
294
- }
295
-
296
- async detectNetworks(region) {
297
- // Uses DescribeVpcsCommand
298
- // Returns standardized network resources
299
- }
300
-
301
- async detectDatabases(region) {
302
- // Uses DescribeDBClustersCommand
303
- // Returns standardized database resources
304
- }
305
-
306
- async detectKeys(region) {
307
- // Uses ListKeysCommand + DescribeKeyCommand
308
- // Returns standardized key resources
309
- }
310
- }
311
- ```
312
-
313
- 📄 See full implementation: `adapters/aws/AWSResourceDetector.js`
314
-
315
- ### Other AWS Adapters
316
-
317
- - **AWSDriftDetector** - Uses CloudFormation drift detection API
318
- - **AWSResourceImporter** - Uses CloudFormation import change sets
319
- - **AWSPropertyReconciler** - Uses CloudFormation update stacks
320
-
321
- 📄 See: `adapters/aws/` directory for all implementations
322
-
323
- ---
324
-
325
- ## Provider Registry
326
-
327
- ### registry.js
328
-
329
- ```javascript
330
- /**
331
- * Provider Registry
332
- *
333
- * Maps provider names to their implementations
334
- */
335
-
336
- class ProviderRegistry {
337
- constructor() {
338
- this.providers = new Map();
339
- this.registerBuiltInProviders();
340
- }
341
-
342
- registerBuiltInProviders() {
343
- // Register AWS (available today)
344
- this.register('aws', require('./aws-provider'));
345
-
346
- // Register GCP (future - throws helpful error)
347
- this.register('gcp', {
348
- name: 'GCP',
349
- available: false,
350
- message: 'GCP support is planned but not yet implemented',
351
- });
352
-
353
- // Register Azure (future - throws helpful error)
354
- this.register('azure', {
355
- name: 'Azure',
356
- available: false,
357
- message: 'Azure support is planned but not yet implemented',
358
- });
359
- }
360
-
361
- register(name, provider) {
362
- this.providers.set(name, provider);
363
- }
364
-
365
- get(name) {
366
- const provider = this.providers.get(name);
367
-
368
- if (!provider) {
369
- throw new Error(`Unknown provider: ${name}. Supported providers: ${Array.from(this.providers.keys()).join(', ')}`);
370
- }
371
-
372
- if (provider.available === false) {
373
- throw new Error(`${provider.name} provider is not yet available. ${provider.message}`);
374
- }
375
-
376
- return provider;
377
- }
378
-
379
- isAvailable(name) {
380
- const provider = this.providers.get(name);
381
- return provider && provider.available !== false;
382
- }
383
-
384
- getSupportedProviders() {
385
- return Array.from(this.providers.keys()).filter(name => this.isAvailable(name));
386
- }
387
- }
388
-
389
- module.exports = new ProviderRegistry();
390
- ```
391
-
392
- ### aws-provider.js
393
-
394
- ```javascript
395
- /**
396
- * AWS Provider Definition
397
- *
398
- * Factory for AWS-specific implementations
399
- */
400
-
401
- const AWSStackRepository = require('../domains/health/infrastructure/adapters/aws/AWSStackRepository');
402
- const AWSResourceDetector = require('../domains/health/infrastructure/adapters/aws/AWSResourceDetector');
403
- const AWSDriftDetector = require('../domains/health/infrastructure/adapters/aws/AWSDriftDetector');
404
- const AWSResourceImporter = require('../domains/health/infrastructure/adapters/aws/AWSResourceImporter');
405
- const AWSPropertyReconciler = require('../domains/health/infrastructure/adapters/aws/AWSPropertyReconciler');
406
-
407
- // Domain builders
408
- const VpcBuilder = require('../domains/networking/aws/vpc-builder');
409
- const KmsBuilder = require('../domains/security/aws/kms-builder');
410
- const AuroraBuilder = require('../domains/database/aws/aurora-builder');
411
- const MigrationBuilder = require('../domains/database/aws/migration-builder');
412
-
413
- module.exports = {
414
- name: 'AWS',
415
- available: true,
416
-
417
- /**
418
- * Create health check adapters for AWS
419
- */
420
- createHealthAdapters({ region }) {
421
- return {
422
- stackRepository: new AWSStackRepository({ region }),
423
- resourceDetector: new AWSResourceDetector({ region }),
424
- driftDetector: new AWSDriftDetector({ region }),
425
- resourceImporter: new AWSResourceImporter({ region }),
426
- propertyReconciler: new AWSPropertyReconciler({ region }),
427
- };
428
- },
429
-
430
- /**
431
- * Get infrastructure builders for AWS
432
- */
433
- getBuilders() {
434
- return [
435
- new VpcBuilder(),
436
- new KmsBuilder(),
437
- new AuroraBuilder(),
438
- new MigrationBuilder(),
439
- ];
440
- },
441
-
442
- /**
443
- * Get supported resource types
444
- */
445
- getSupportedResourceTypes() {
446
- return [
447
- 'AWS::EC2::VPC',
448
- 'AWS::EC2::Subnet',
449
- 'AWS::EC2::SecurityGroup',
450
- 'AWS::RDS::DBCluster',
451
- 'AWS::RDS::DBInstance',
452
- 'AWS::KMS::Key',
453
- 'AWS::Lambda::Function',
454
- 'AWS::SQS::Queue',
455
- 'AWS::S3::Bucket',
456
- // ... more
457
- ];
458
- },
459
-
460
- /**
461
- * Get resource property metadata
462
- */
463
- getResourceMetadata(resourceType) {
464
- return require(`./metadata/${resourceType.replace(/::/g, '_')}.json`);
465
- },
466
- };
467
- ```
468
-
469
- ---
470
-
471
- ## Use Case Integration with Providers
472
-
473
- ### RunHealthCheckUseCase
474
-
475
- ```javascript
476
- const ProviderRegistry = require('../../../providers/registry');
477
-
478
- class RunHealthCheckUseCase {
479
- /**
480
- * @param {Object} dependencies - Injected dependencies (optional)
481
- */
482
- constructor(dependencies = {}) {
483
- this.dependencies = dependencies;
484
- }
485
-
486
- /**
487
- * Execute health check
488
- *
489
- * @param {Object} params
490
- * @param {string} params.stackName - CloudFormation stack name
491
- * @param {string} params.region - Cloud provider region
492
- * @param {string} params.provider - Provider name ('aws', 'gcp', 'azure')
493
- * @param {Object} params.appDefinition - App definition (desired state)
494
- */
495
- async execute({ stackName, region, provider, appDefinition }) {
496
- // Get provider-specific adapters
497
- const providerImpl = ProviderRegistry.get(provider);
498
- const adapters = this.dependencies.adapters || providerImpl.createHealthAdapters({ region });
499
-
500
- // Step 1: Get stack state
501
- const stackIdentifier = new StackIdentifier({ stackName, region });
502
- const stack = await adapters.stackRepository.getStack(stackIdentifier);
503
-
504
- if (!stack) {
505
- return StackHealthReport.createForMissingStack(stackIdentifier);
506
- }
507
-
508
- // Step 2: Discover resources
509
- const stackResources = await adapters.stackRepository.listResources(stackIdentifier);
510
- const cloudResources = await this.discoverCloudResources(adapters.resourceDetector, region);
511
-
512
- // Step 3: Detect issues
513
- const orphanedResources = this.detectOrphaned(stackResources, cloudResources);
514
- const missingResources = this.detectMissing(stackResources, cloudResources);
515
- const driftedResources = await this.detectDrift(stackResources, cloudResources, adapters.driftDetector);
516
-
517
- // Step 4: Calculate health score
518
- const healthScore = HealthScoreCalculator.calculate({
519
- orphaned: orphanedResources.length,
520
- missing: missingResources.length,
521
- drifted: driftedResources.length,
522
- total: stackResources.length,
523
- });
524
-
525
- // Step 5: Build health report
526
- return new StackHealthReport({
527
- stackIdentifier,
528
- healthScore,
529
- orphanedResources,
530
- missingResources,
531
- driftedResources,
532
- });
533
- }
534
-
535
- async discoverCloudResources(detector, region) {
536
- const [networks, databases, keys] = await Promise.all([
537
- detector.detectNetworks(region),
538
- detector.detectDatabases(region),
539
- detector.detectKeys(region),
540
- ]);
541
-
542
- return [...networks, ...databases, ...keys];
543
- }
544
-
545
- detectOrphaned(stackResources, cloudResources) {
546
- // Resources in cloud but not in stack
547
- const stackPhysicalIds = new Set(stackResources.map(r => r.physicalId));
548
- return cloudResources.filter(r => !stackPhysicalIds.has(r.physicalId));
549
- }
550
-
551
- detectMissing(stackResources, cloudResources) {
552
- // Resources in stack but not in cloud
553
- const cloudPhysicalIds = new Set(cloudResources.map(r => r.physicalId));
554
- return stackResources.filter(r => !cloudPhysicalIds.has(r.physicalId));
555
- }
556
-
557
- async detectDrift(stackResources, cloudResources, driftDetector) {
558
- const drifted = [];
559
-
560
- for (const stackResource of stackResources) {
561
- const cloudResource = cloudResources.find(r => r.physicalId === stackResource.physicalId);
562
-
563
- if (cloudResource) {
564
- const mismatches = await driftDetector.detectDrift(stackResource, cloudResource.properties);
565
-
566
- if (mismatches.length > 0) {
567
- drifted.push({
568
- resource: stackResource,
569
- mismatches,
570
- });
571
- }
572
- }
573
- }
574
-
575
- return drifted;
576
- }
577
- }
578
-
579
- module.exports = RunHealthCheckUseCase;
580
- ```
581
-
582
- ---
583
-
584
- ## CLI Command Integration
585
-
586
- ### doctor-command.js
587
-
588
- ```javascript
589
- const ProviderRegistry = require('../../providers/registry');
590
- const RunHealthCheckUseCase = require('../../domains/health/application/use-cases/run-health-check-use-case');
591
- const HealthReportPresenter = require('./presenters/health-report-presenter');
592
-
593
- async function doctorCommand(options) {
594
- console.log('🩺 Running infrastructure health check...\n');
595
-
596
- // Load app definition
597
- const appDefinition = loadAppDefinition();
598
- const provider = options.provider || appDefinition.provider || 'aws';
599
- const region = options.region || appDefinition.region;
600
- const stackName = options.stack || `${appDefinition.name}-${options.stage || 'dev'}`;
601
-
602
- // Verify provider is supported
603
- if (!ProviderRegistry.isAvailable(provider)) {
604
- console.error(`❌ Provider '${provider}' is not supported`);
605
- console.log(`Supported providers: ${ProviderRegistry.getSupportedProviders().join(', ')}`);
606
- process.exit(1);
607
- }
608
-
609
- // Create use case (adapters created automatically based on provider)
610
- const useCase = new RunHealthCheckUseCase();
611
-
612
- // Execute health check
613
- const report = await useCase.execute({
614
- stackName,
615
- region,
616
- provider,
617
- appDefinition,
618
- });
619
-
620
- // Present results
621
- const presenter = new HealthReportPresenter({ format: options.format || 'table' });
622
- presenter.present(report);
623
-
624
- // Exit with code based on health score (if requested)
625
- if (options.exitCode) {
626
- if (report.healthScore.isHealthy()) {
627
- process.exit(0);
628
- } else if (report.healthScore.isDegraded()) {
629
- process.exit(1);
630
- } else {
631
- process.exit(2);
632
- }
633
- }
634
- }
635
-
636
- module.exports = doctorCommand;
637
- ```
638
-
639
- ---
640
-
641
- ## Extension Points for Future Providers
642
-
643
- ### Adding GCP Support
644
-
645
- To add GCP support in the future:
646
-
647
- 1. **Create GCP adapters**:
648
- ```
649
- infrastructure/domains/health/infrastructure/adapters/gcp/
650
- ├── GCPStackRepository.js # Deployment Manager
651
- ├── GCPResourceDetector.js # GCP APIs
652
- ├── GCPDriftDetector.js
653
- ├── GCPResourceImporter.js
654
- └── GCPPropertyReconciler.js
655
- ```
656
-
657
- 2. **Create GCP builders**:
658
- ```
659
- infrastructure/domains/networking/gcp/network-builder.js
660
- infrastructure/domains/database/gcp/cloud-sql-builder.js
661
- infrastructure/domains/security/gcp/kms-builder.js
662
- ```
663
-
664
- 3. **Register GCP provider**:
665
- ```javascript
666
- // providers/gcp-provider.js
667
- module.exports = {
668
- name: 'GCP',
669
- available: true,
670
- createHealthAdapters({ region }) { ... },
671
- getBuilders() { ... },
672
- getSupportedResourceTypes() { ... },
673
- };
674
- ```
675
-
676
- 4. **Update registry**:
677
- ```javascript
678
- // providers/registry.js
679
- this.register('gcp', require('./gcp-provider'));
680
- ```
681
-
682
- **No changes required to**:
683
- - Domain entities (Resource, Issue, StackHealthReport)
684
- - Value objects (HealthScore, ResourceState)
685
- - Use cases (RunHealthCheckUseCase, RepairStackViaImportUseCase)
686
- - CLI commands (doctor-command.js, repair-command.js)
687
-
688
- The hexagonal architecture ensures new providers only require implementing the port interfaces!
689
-
690
- ---
691
-
692
- ## Summary
693
-
694
- This architecture achieves:
695
-
696
- ✅ **Multi-cloud ready** - Ports & Adapters make provider swapping trivial
697
- ✅ **Provider-specific domains clear** - Obvious where AWS/GCP/Azure diverge
698
- ✅ **Testable** - Mock port interfaces for unit tests
699
- ✅ **Extensible** - Add new providers without touching domain logic
700
- ✅ **Explicit** - Provider selection obvious in app definition
701
- ✅ **Future-proof** - Non-serverless (Docker) can be added as another provider
702
-
703
- **AWS works today**, and the path to GCP/Azure/Cloudflare is clear and isolated to adapter implementations.