|
| 1 | +--- |
| 2 | +created: '2025-10-24' |
| 3 | +sourceSHA: TBD |
| 4 | +--- |
| 5 | + |
| 6 | +# Component Implementation Strategy: Unified Component vs Dedicated Component |
| 7 | + |
| 8 | +## Background |
| 9 | + |
| 10 | +When building product features on top of Tekton community capabilities, we often encounter requirements for advanced features that need enhancement or extension. These requirements may stem from: |
| 11 | + |
| 12 | +- Enterprise-level feature requirements (such as advanced permission control, audit logs, etc.) |
| 13 | +- Product differentiation features (such as specific UI interactions, business process customization, etc.) |
| 14 | +- Deep integration with internal systems (such as integration with enterprise LDAP, private Git systems, etc.) |
| 15 | +- Performance or scale optimization (such as special optimizations for large-scale scenarios) |
| 16 | + |
| 17 | +These capabilities are often not suitable for direct feedback to the community, for reasons such as: |
| 18 | + |
| 19 | +1. Too customized, lacking universality |
| 20 | +2. Related to enterprise internal systems |
| 21 | +3. Involving technology-specific routes or proprietary technology |
| 22 | +4. Implementation approach differs from community design philosophy |
| 23 | + |
| 24 | +When we decide to develop these productized features, we face an architectural choice: should we add new features to a **unified multi-responsibility component**, or create **dedicated single-responsibility components** for each feature? |
| 25 | + |
| 26 | +### Concept Clarification |
| 27 | + |
| 28 | +- **Unified Component**: A comprehensive service that internally contains multiple Controllers or modules, responsible for handling multiple different domain functions. For example, a component that simultaneously handles Pipeline enhancement, Trigger extension, Result management, and other responsibilities, typically using a lazy-loading mechanism to enable relevant Controllers on demand. |
| 29 | + |
| 30 | +- **Dedicated Component**: A single-responsibility service that focuses on solving problems in a specific domain. For example, the TriggerWrapper component that specifically handles Trigger enhancement, or the AuditLog component that specifically handles audit logging. |
| 31 | + |
| 32 | +This document aims to provide decision-making guidance to help teams choose the most appropriate component architecture approach in different scenarios. |
| 33 | + |
| 34 | +## Core Principles |
| 35 | + |
| 36 | +When making implementation decisions, we should follow these core principles: |
| 37 | + |
| 38 | +1. **Single Responsibility Principle**: The scope of component responsibilities should be clear and easy to understand and maintain |
| 39 | +2. **Reasonable Boundaries**: Find a balance between service granularity and operational complexity |
| 40 | +3. **Maintainability First**: Consider long-term maintenance costs, not just short-term development efficiency |
| 41 | +4. **Independent Evolution Capability**: Features should be able to be released and upgraded independently |
| 42 | +5. **Team Organization Alignment**: Component division should match team organizational structure |
| 43 | + |
| 44 | +## Decision Framework |
| 45 | + |
| 46 | +### When to Choose Unified Component Implementation |
| 47 | + |
| 48 | +**Unified Component Implementation** refers to integrating multiple productized features into a comprehensive service, where the service internally contains multiple Controllers or modules that can simultaneously handle enhancement requirements in different domains. |
| 49 | + |
| 50 | +**Lazy-loading Mechanism Implementation**: |
| 51 | + |
| 52 | +A typical lazy-loading mechanism works through the following approach: |
| 53 | + |
| 54 | +1. **CRD Detection**: At startup, check which Tekton CRDs are installed in the cluster (e.g., Pipeline, Trigger, Result) |
| 55 | +2. **Conditional Controller Registration**: Only register and start Controllers for installed CRDs |
| 56 | +3. **Dynamic Discovery**: Use Kubernetes Discovery API to query available resource types |
| 57 | +4. **Graceful Skip**: If a required CRD is not found, log a message and skip that Controller without failing startup |
| 58 | +5. **Watch and React**: Optionally watch for CRD installation/removal events and dynamically enable/disable Controllers |
| 59 | + |
| 60 | +**Advantages of Lazy-loading Mechanism**: |
| 61 | + |
| 62 | +1. **Automatic Adaptation**: Automatically enables corresponding enhancement features based on actually deployed Tekton components |
| 63 | +2. **Resource Optimization**: Does not start unused Controllers, saving resources |
| 64 | +3. **Flexible Deployment**: Users can selectively deploy Tekton components without manual configuration |
| 65 | +4. **Error Reduction**: Avoids startup failures due to non-existent CRDs |
| 66 | + |
| 67 | +#### Advantages |
| 68 | + |
| 69 | +- **Simple Deployment**: Only need to deploy one service, reducing deployment configuration complexity |
| 70 | +- **High Resource Efficiency**: Shares process space, memory, and connection pools, reducing resource consumption |
| 71 | +- **Good Code Reuse**: Common logic (such as Kubernetes Client, logging, monitoring, etc.) only needs to be implemented once |
| 72 | +- **Fast Initial Development**: No need to define and maintain interfaces between multiple services |
| 73 | +- **Relatively Simple Debugging**: Related logic is within one process, making debugging and problem localization relatively easy |
| 74 | +- **Simple Dependency Management**: All features use unified dependency versions, avoiding version conflicts |
| 75 | +- **Transactional Support**: Multiple features can be coordinated in the same context, ensuring consistency |
| 76 | + |
| 77 | +#### Disadvantages |
| 78 | + |
| 79 | +- **Unclear Responsibilities**: Component takes on too many responsibilities, violating single responsibility principle |
| 80 | +- **Code Coupling Risk**: Code for different features in the same repository can easily lead to unexpected coupling |
| 81 | +- **Limited Scalability**: Cannot scale different features independently, can only scale as a whole |
| 82 | +- **Release Coupling**: A change to one feature requires redeploying the entire component, affecting other features |
| 83 | +- **High Testing Complexity**: Need to test interactions and isolation between features |
| 84 | +- **Team Collaboration Difficulties**: When multiple teams modify the same codebase, conflicts are easy to occur (if teams expand later) |
| 85 | +- **Failure Propagation Risk**: A bug in one feature may affect the entire component, such as memory leaks |
| 86 | +- **High Cognitive Burden**: New members may need to understand all features of the entire component to work effectively |
| 87 | + |
| 88 | +### When to Choose Dedicated Component Implementation |
| 89 | + |
| 90 | +**Dedicated Component Implementation** refers to creating a single-responsibility, clearly-bounded independent service for a specific feature, focusing on solving problems in a specific domain. |
| 91 | + |
| 92 | +#### Applicable Scenarios |
| 93 | + |
| 94 | +1. **Feature is Mature and Stable** |
| 95 | + - Feature has been validated, boundaries are clear |
| 96 | + - Expected to exist in the product long-term |
| 97 | + - Worth investing additional engineering costs |
| 98 | + |
| 99 | + **Examples**: |
| 100 | + - Core features validated in production environment |
| 101 | + - Key features that users depend on |
| 102 | + - Services that need to provide SLA guarantees |
| 103 | + |
| 104 | +2. **Need Independent Scaling Strategy** |
| 105 | + - Feature has unique performance characteristics (such as high concurrency, high memory, CPU intensive, etc.) |
| 106 | + - Need to scale up/down independently based on load |
| 107 | + - Resource requirements differ significantly from other features |
| 108 | + |
| 109 | + **Examples**: |
| 110 | + - High-concurrency Webhook gateway (requires many instances) |
| 111 | + - Machine learning inference service (requires GPU) |
| 112 | + - Log aggregation service (requires large amounts of memory and disk I/O) |
| 113 | + |
| 114 | +3. **Cross-team or Cross-project Reuse** |
| 115 | + - Feature may be used by multiple teams or projects |
| 116 | + - Want to provide as platform capability |
| 117 | + - May be open-sourced or commercialized in the future |
| 118 | + |
| 119 | + **Examples**: |
| 120 | + - Universal Webhook gateway service |
| 121 | + - Pipeline template marketplace |
| 122 | + - Reusable audit log system |
| 123 | + |
| 124 | +4. **Different Availability Requirements** |
| 125 | + - Feature availability requirements vary significantly |
| 126 | + - Failure of certain features should not affect other features |
| 127 | + - Need independent fault tolerance and recovery strategies |
| 128 | + |
| 129 | + **Examples**: |
| 130 | + - Core Pipeline execution (high availability) vs statistical analysis (can tolerate brief unavailability) |
| 131 | + - Real-time triggers (high availability) vs offline reports (low availability) |
| 132 | + - Critical audit (high availability) vs experimental features (low availability) |
| 133 | + |
| 134 | +#### Advantages |
| 135 | + |
| 136 | +- **Clear Responsibilities**: Each component only handles a specific functional domain, conforming to single responsibility principle |
| 137 | +- **Independent Evolution**: Can have its own version, release cadence, and technology choices |
| 138 | +- **Failure Isolation**: Failure of one component does not affect other components |
| 139 | +- **Independent Scaling**: Can scale up/down independently based on respective load characteristics |
| 140 | +- **Team Autonomy**: Different teams can develop in parallel, reducing coordination costs |
| 141 | +- **Technical Flexibility**: Can choose the most appropriate technology stack for each feature |
| 142 | +- **Simple Testing**: Component boundaries are clear, testing scope is well-defined |
| 143 | +- **Good Reusability**: Single-responsibility components are easier to reuse in other scenarios |
| 144 | +- **Low Cognitive Burden**: New members only need to understand a single component's responsibilities to start working |
| 145 | +- **Open Source Friendly**: Easier to independently open source or contribute features to the community |
| 146 | + |
| 147 | +#### Disadvantages |
| 148 | + |
| 149 | +- **High Operational Complexity**: Need to deploy, monitor, and maintain multiple services |
| 150 | +- **High Resource Consumption**: Each service requires independent Pod, memory, CPU and other resources |
| 151 | +- **Network Overhead**: Inter-component communication requires network calls, increasing latency |
| 152 | +- **Complex Debugging**: Problem troubleshooting needs to span multiple services, requiring comprehensive distributed tracing |
| 153 | +- **Consistency Challenges**: Distributed system consistency issues, such as transaction coordination, state synchronization, etc. |
| 154 | +- **Interface Management Cost**: Need to define and maintain interfaces and contracts between components |
| 155 | +- **Deployment Coordination**: Deployment and upgrade of multiple components need coordination, increasing release complexity |
| 156 | +- **Slower Initial Development**: Need additional work to design interfaces, handle communication, implement fault tolerance, etc. |
| 157 | +- **Version Compatibility**: Need to maintain version compatibility matrix between components |
| 158 | + |
| 159 | +## Decision Checklist |
| 160 | + |
| 161 | +When making implementation decisions, you can refer to the following checklist: |
| 162 | + |
| 163 | +### Favor Unified Component |
| 164 | + |
| 165 | +- [ ] Project is in early stage, feature boundaries are not yet clear |
| 166 | +- [ ] Multiple features share same technology stack (all are Kubernetes Controllers) |
| 167 | +- [ ] Features need to frequently share state or coordinate |
| 168 | +- [ ] Cluster resources are limited, want to reduce Pod count |
| 169 | +- [ ] Features are in exploration stage, may be adjusted or removed at any time |
| 170 | +- [ ] All features are maintained by the same team |
| 171 | +- [ ] Features have no special compute resource requirements |
| 172 | + |
| 173 | +### Favor Dedicated Component |
| 174 | + |
| 175 | +- [ ] Feature has clear domain boundaries and responsibility definition |
| 176 | +- [ ] Feature is mature, expected to exist long-term |
| 177 | +- [ ] Need independent scaling strategy (such as special compute resource requirements) |
| 178 | +- [ ] Feature may be reused by other projects or teams, or may be open-sourced |
| 179 | +- [ ] Feature availability requirements vary significantly |
| 180 | +- [ ] Can accept additional operational costs |
| 181 | +- [ ] Have sufficient operational resources to support multiple services |
| 182 | + |
| 183 | +### Risk Assessment |
| 184 | + |
| 185 | +#### Unified Component Risks |
| 186 | + |
| 187 | +- **High Risk Signals**: |
| 188 | + - Different features are maintained by different teams |
| 189 | + - Features are heavily coupled, difficult to test |
| 190 | + - Frequently one feature change affects other features |
| 191 | + |
| 192 | +#### Dedicated Component Risks |
| 193 | + |
| 194 | +- **High Risk Signals**: |
| 195 | + - Too many independent services to manage (> 10) |
| 196 | + - Complex dependency relationships between components |
| 197 | + - Lack of unified monitoring and log aggregation |
| 198 | + - Version compatibility management is chaotic |
| 199 | + - Troubleshooting often needs to span multiple components |
| 200 | + |
| 201 | +## Best Practice Recommendations |
| 202 | + |
| 203 | +### 1. Start with Unified Component, Split When Appropriate |
| 204 | + |
| 205 | +- **Early Stage**: Prioritize using unified component to quickly validate feasibility of multiple features |
| 206 | +- **Growth Stage**: Identify mature features with clear boundaries, evaluate feasibility of splitting |
| 207 | +- **Mature Stage**: Split core features into dedicated components, keep exploratory features in unified component |
| 208 | + |
| 209 | +**Determining When to Split**: |
| 210 | +- Feature iteration frequency is significantly higher than other features |
| 211 | +- Feature is managed by an independent team |
| 212 | +- Feature has unique scaling requirements |
| 213 | +- Feature boundaries have stabilized |
| 214 | + |
| 215 | +### 2. Always Maintain Good Module Boundaries |
| 216 | + |
| 217 | +Even when choosing unified component, maintain good code organization: |
| 218 | + |
| 219 | +**Recommended Architecture: Hybrid Deployment Model** |
| 220 | + |
| 221 | +A well-designed unified component repository should support both all-in-one deployment and independent component deployment: |
| 222 | + |
| 223 | +``` |
| 224 | +tektoncd-enhancement/ |
| 225 | +βββ cmd/ |
| 226 | +β βββ all-in-one/ # All-in-one entry point |
| 227 | +β βββ main.go # Imports and starts all controllers |
| 228 | +βββ pipeline-enhancer/ # Independent controller #1 |
| 229 | +β βββ cmd/ |
| 230 | +β β βββ main.go # Can build standalone |
| 231 | +β βββ pkg/ |
| 232 | +β β βββ controller/ |
| 233 | +β β βββ controller.go |
| 234 | +β βββ go.mod # Independent go module (optional) |
| 235 | +βββ trigger-wrapper/ # Independent controller #2 |
| 236 | +β βββ cmd/ |
| 237 | +β β βββ main.go # Can build standalone |
| 238 | +β βββ pkg/ |
| 239 | +β β βββ controller/ |
| 240 | +β β βββ controller.go |
| 241 | +β βββ go.mod # Independent go module (optional) |
| 242 | +βββ result-manager/ # Independent controller #3 |
| 243 | +β βββ cmd/ |
| 244 | +β β βββ main.go # Can build standalone |
| 245 | +β βββ pkg/ |
| 246 | +β β βββ controller/ |
| 247 | +β β βββ controller.go |
| 248 | +β βββ go.mod # Independent go module (optional) |
| 249 | +βββ shared/ # Shared utilities (optional) |
| 250 | + βββ pkg/ |
| 251 | + βββ client/ |
| 252 | + βββ utils/ |
| 253 | +``` |
| 254 | + |
| 255 | +**Avoid**: |
| 256 | +- Mixing code for different features in one file |
| 257 | +- Having features directly call each other's internal methods |
| 258 | +- Sharing mutable state across controllers |
| 259 | +- Creating circular dependencies between subdirectories |
| 260 | + |
| 261 | +### 3. Define Clear Interface Contracts |
| 262 | + |
| 263 | +Whether unified or dedicated components, define clear interfaces: |
| 264 | + |
| 265 | +```go |
| 266 | +// Even within unified component, communicate through interfaces |
| 267 | +type PipelineEnhancer interface { |
| 268 | + EnhancePipeline(ctx context.Context, pipeline *v1.Pipeline) error |
| 269 | +} |
| 270 | + |
| 271 | +type TriggerEnhancer interface { |
| 272 | + EnhanceTrigger(ctx context.Context, trigger *v1.Trigger) error |
| 273 | +} |
| 274 | +``` |
| 275 | + |
| 276 | +This facilitates future splitting or reorganization. |
| 277 | + |
| 278 | +## Summary |
| 279 | + |
| 280 | +### Recommended Approach: Start Unified, Split When Necessary |
| 281 | + |
| 282 | +**Default Choice: Unified Component** |
| 283 | + |
| 284 | +We recommend **prioritizing the unified component pattern** as the default implementation approach for new features. This strategy offers: |
| 285 | + |
| 286 | +- **Lower Initial Barrier**: Faster development and deployment in early stages |
| 287 | +- **Resource Efficiency**: Reduced operational overhead and resource consumption |
| 288 | +- **Flexibility**: Can evolve into dedicated components when needed |
| 289 | + |
| 290 | +**When to Consider Dedicated Components** |
| 291 | + |
| 292 | +Use the decision checklist to regularly evaluate your implementation. Consider splitting into dedicated components when: |
| 293 | + |
| 294 | +1. **Special Requirements Emerge**: Feature shows characteristics from the "Favor Dedicated Component" checklist |
| 295 | +2. **Risk Signals Appear**: Hit high-risk indicators in the unified component risk assessment |
| 296 | +3. **Team Structure Changes**: Different teams need to own different features |
| 297 | +4. **Scale Requirements Differ**: Specific features need independent scaling strategies |
| 298 | + |
| 299 | +**Migration Path** |
| 300 | + |
| 301 | +The recommended evolution path is: |
| 302 | + |
| 303 | +``` |
| 304 | +Phase 1: Unified Component (Default) |
| 305 | + β |
| 306 | +Phase 2: Regular Evaluation (Use Checklist) |
| 307 | + β |
| 308 | +Phase 3: Gradual Migration (Split mature features when necessary) |
| 309 | + β |
| 310 | +Phase 4: Hybrid Model (All-in-one + Dedicated) |
| 311 | +``` |
| 312 | + |
| 313 | +**Key Principle** |
| 314 | + |
| 315 | +> **"Start simple, evolve deliberately"** - Begin with unified component for agility, split into dedicated components only when clear benefits outweigh the added complexity. |
| 316 | +
|
| 317 | +By following this pragmatic approach, teams can balance rapid development with long-term maintainability, avoiding both premature optimization and technical debt accumulation. |
| 318 | + |
| 319 | + |
0 commit comments