@@ -16,13 +16,13 @@ The codebase follows several key principles:
1616
1717```
1818stackstate-backup-cli/
19- ├── cmd/ # Command-line interface (Layer 3 )
19+ ├── cmd/ # Command-line interface (Layer 4 )
2020│ ├── root.go # Root command and global flags
2121│ ├── version/ # Version information command
2222│ ├── elasticsearch/ # Elasticsearch backup/restore commands
2323│ └── stackgraph/ # Stackgraph backup/restore commands
2424│
25- ├── internal/ # Internal packages (Layers 0-2 )
25+ ├── internal/ # Internal packages (Layers 0-3 )
2626│ ├── foundation/ # Layer 0: Core utilities
2727│ │ ├── config/ # Configuration management
2828│ │ ├── logger/ # Structured logging
@@ -37,6 +37,9 @@ stackstate-backup-cli/
3737│ │ ├── portforward/ # Port-forwarding orchestration
3838│ │ └── scale/ # Deployment scaling workflows
3939│ │
40+ │ ├── app/ # Layer 3: Dependency Container
41+ │ │ └── app.go # Application context and dependency injection
42+ │ │
4043│ └── scripts/ # Embedded bash scripts
4144│
4245├── main.go # Application entry point
@@ -46,24 +49,61 @@ stackstate-backup-cli/
4649
4750## Architectural Layers
4851
49- ### Layer 3 : Commands (` cmd/ ` )
52+ ### Layer 4 : Commands (` cmd/ ` )
5053
5154** Purpose** : User-facing CLI commands and application entry points
5255
5356** Characteristics** :
5457- Implements the Cobra command structure
5558- Handles user input validation and flag parsing
56- - Orchestrates calls to lower layers
59+ - Delegates to orchestration and client layers via app context
60+ - Minimal business logic (thin command layer)
5761- Formats output for end users
5862
5963** Key Packages** :
6064- ` cmd/elasticsearch/ ` : Elasticsearch snapshot/restore commands (configure, list-snapshots, list-indices, restore-snapshot)
6165- ` cmd/stackgraph/ ` : Stackgraph backup/restore commands (list, restore)
6266- ` cmd/version/ ` : Version information
6367
68+ ** Dependency Rules** :
69+ - ✅ Can import: ` internal/app/* ` (preferred), all other ` internal/ ` packages
70+ - ❌ Should not: Create clients directly, contain business logic
71+
72+ ### Layer 3: Dependency Container (` internal/app/ ` )
73+
74+ ** Purpose** : Centralized dependency initialization and injection
75+
76+ ** Characteristics** :
77+ - Creates and wires all application dependencies
78+ - Provides single entry point for dependency creation
79+ - Eliminates boilerplate from commands
80+ - Improves testability through centralized mocking
81+
82+ ** Key Components** :
83+ - ` Context ` : Struct holding all dependencies (K8s client, S3 client, ES client, config, logger, formatter)
84+ - ` NewContext() ` : Factory function creating production dependencies from global flags
85+
86+ ** Usage Pattern** :
87+ ``` go
88+ // In command files
89+ appCtx , err := app.NewContext (globalFlags)
90+ if err != nil {
91+ return err
92+ }
93+
94+ // All dependencies available via appCtx
95+ appCtx.K8sClient
96+ appCtx.S3Client
97+ appCtx.ESClient
98+ appCtx.Config
99+ appCtx.Logger
100+ appCtx.Formatter
101+ ```
102+
64103** Dependency Rules** :
65104- ✅ Can import: All ` internal/ ` packages
66- - ❌ Should not: Contain business logic or direct service calls
105+ - ✅ Used by: ` cmd/ ` layer only
106+ - ❌ Should not: Contain business logic or orchestration
67107
68108### Layer 2: Orchestration (` internal/orchestration/ ` )
69109
@@ -130,31 +170,63 @@ stackstate-backup-cli/
130170 └─> cmd/elasticsearch/restore-snapshot.go
131171 │
1321722. Parse flags and validate input
173+ └─> Cobra command receives global flags
133174 │
134- 3. Load configuration
135- └─> internal/foundation/config/
136- │
137- 4. Create clients
138- └─> internal/clients/k8s/
139- └─> internal/clients/elasticsearch/
175+ 3. Create application context with dependencies
176+ └─> app.NewContext(globalFlags)
177+ ├─> internal/clients/k8s/ (K8s client)
178+ ├─> internal/foundation/config/ (Load from ConfigMap/Secret)
179+ ├─> internal/clients/s3/ (S3/Minio client)
180+ ├─> internal/clients/elasticsearch/ (ES client)
181+ ├─> internal/foundation/logger/ (Logger)
182+ └─> internal/foundation/output/ (Formatter)
140183 │
141- 5. Execute orchestration workflow
142- └─> internal/orchestration/scale/
143- └─> Scale down deployments
144- └─> internal/orchestration/portforward/
145- └─> Setup port-forward to Elasticsearch
146- └─> internal/clients/elasticsearch/
147- └─> Perform snapshot restore
148- └─> internal/orchestration/scale/
149- └─> Scale up deployments
184+ 4. Execute business logic with injected dependencies
185+ └─> runRestore(appCtx)
186+ ├─> internal/orchestration/scale/ (Scale down)
187+ ├─> internal/orchestration/portforward/ (Port-forward)
188+ ├─> internal/clients/elasticsearch/ (Restore snapshot)
189+ └─> internal/orchestration/scale/ (Scale up)
150190 │
151- 6 . Format and display results
152- └─> internal/foundation/output/
191+ 5 . Format and display results
192+ └─> appCtx.Formatter.PrintTable() or PrintJSON()
153193```
154194
155195## Key Design Patterns
156196
157- ### 1. Configuration Precedence
197+ ### 1. Dependency Injection Pattern
198+
199+ All dependencies are created once and injected via ` app.Context ` :
200+
201+ ``` go
202+ // Before (repeated in every command)
203+ func runList (globalFlags *config .CLIGlobalFlags ) error {
204+ k8sClient , _ := k8s.NewClient (...)
205+ cfg , _ := config.LoadConfig (...)
206+ s3Client , _ := s3.NewClient (...)
207+ log := logger.New (...)
208+ formatter := output.NewFormatter (...)
209+ // ... use dependencies
210+ }
211+
212+ // After (centralized creation)
213+ func runList (appCtx *app .Context ) error {
214+ // All dependencies available immediately
215+ appCtx.K8sClient
216+ appCtx.Config
217+ appCtx.S3Client
218+ appCtx.Logger
219+ appCtx.Formatter
220+ }
221+ ```
222+
223+ ** Benefits** :
224+ - Eliminates boilerplate from commands (30-50 lines per command)
225+ - Centralized dependency creation makes testing easier
226+ - Single source of truth for dependency wiring
227+ - Commands are thinner and more focused on business logic
228+
229+ ### 2. Configuration Precedence
158230
159231Configuration is loaded with the following precedence (highest to lowest):
160232
@@ -166,7 +238,7 @@ Configuration is loaded with the following precedence (highest to lowest):
166238
167239Implementation: ` internal/foundation/config/config.go `
168240
169- ### 2 . Client Factory Pattern
241+ ### 3 . Client Factory Pattern
170242
171243Clients are created with a consistent factory pattern:
172244
@@ -178,7 +250,7 @@ func NewClient(endpoint string) (*Client, error) {
178250}
179251```
180252
181- ### 3 . Port-Forward Lifecycle
253+ ### 4 . Port-Forward Lifecycle
182254
183255Services running in Kubernetes are accessed via automatic port-forwarding:
184256
@@ -188,7 +260,7 @@ pf, err := SetupPortForward(k8sClient, namespace, service, localPort, remotePort
188260defer close (pf.StopChan ) // Automatic cleanup
189261```
190262
191- ### 4 . Scale Down/Up Pattern
263+ ### 5 . Scale Down/Up Pattern
192264
193265Deployments are scaled down before restore operations and scaled up afterward:
194266
@@ -198,7 +270,7 @@ scaledDeployments, _ := scale.ScaleDown(k8sClient, namespace, selector, log)
198270defer scale.ScaleUp (k8sClient, namespace, scaledDeployments, log)
199271```
200272
201- ### 5 . Structured Logging
273+ ### 6 . Structured Logging
202274
203275All operations use structured logging with consistent levels:
204276
@@ -292,6 +364,27 @@ endpoint := "http://localhost:9200"
292364
293365** Fix** : Use configuration management: ` config.Elasticsearch.Service.Name `
294366
367+ ### ❌ Don't: Create Clients Directly in Commands
368+
369+ ``` go
370+ // BAD: cmd/elasticsearch/list-snapshots.go
371+ func runListSnapshots (globalFlags *config .CLIGlobalFlags ) error {
372+ k8sClient , _ := k8s.NewClient (globalFlags.Kubeconfig , globalFlags.Debug )
373+ esClient , _ := elasticsearch.NewClient (" http://localhost:9200" )
374+ // ... use clients
375+ }
376+ ```
377+
378+ ** Fix** : Use ` app.Context ` for dependency injection:
379+ ``` go
380+ // GOOD
381+ func runListSnapshots (appCtx *app .Context ) error {
382+ // Dependencies already created
383+ appCtx.K8sClient
384+ appCtx.ESClient
385+ }
386+ ```
387+
295388## Automated Enforcement
296389
297390Verify architectural rules with these commands:
0 commit comments