Skip to content

[Feature] Implement Dependency Graph for Resource Planning and Execution #156

@Blankll

Description

@Blankll

Context

As part of the migration to a Terraform-like unified state engine (see #124 and #154), resource deployment, update, and destruction for Aliyun and all providers are moving towards provider-agnostic, functional planning and execution. However, current deployment order is hardcoded by resource type (functions first, then buckets, events, etc). This can lead to:

  • Missing or incorrect dependency handling between resources (e.g., function must exist before event can reference it, table depends on database)
  • Inability to visualize why certain resources were created/updated/deleted in that order
  • Lack of fine-grained error reporting and partial application if dependencies are not met

Tools like Terraform use an explicit Resource Dependency Graph (DAG) to:

  • Plan apply/destroy exactly in dependency order
  • Allow parallelization where no direct dependency exists
  • Enable detailed visualizations (graphviz, DOT, etc) of resource relationships
  • Support inter-resource references (outputs -> inputs)
  • Robustly handle partial failures, rollbacks, and reordering

Requirements / Acceptance Criteria

  • Implement a dependency graph engine that auto-detects resource dependencies (e.g., event references function, bucket policies reference functions, etc) from the YAML/IaC model
  • Use the graph to determine resource planning and execution order for plan/apply/destroy
  • Plan/execute all resource changes in topological order, supporting parallel execution where possible
  • On plan/drift, show the dependency structure and which resources would be affected by a change/failure (optionally: visualize via graphviz or DOT file)
  • Engine should validate and catch cycles (circular dependencies) with clear errors

Additional Context

Coding/Architecture Guidelines

  • Use functional TypeScript: define functions as const xxx = (...) => .... Prefer functional decomposition over OOP; avoid classes unless strictly necessary.
  • Prefer declarative/functional collection handling: replace for/while loops with map, filter, find, some, every, reduce, flatMap (and sort when appropriate). Favor pipeline-style transformations over step-by-step imperative logic.
  • Favor immutability: avoid in-place mutation (push, splice, mutating objects/arrays, shared mutable state). Instead, return new arrays/objects and model changes as explicit state-transform functions (e.g., reducers).
  • Prefer pure functions: keep functions small, composable, and side-effect-free where possible. If effects are required (I/O, logging), isolate them at the boundaries and keep core logic pure.
  • Types: prefer type/enum over interface where possible; use type when it can fully replace an interface.
  • Module boundaries: each module should export only via its index.ts; avoid deep imports.
  • Export discipline: only export functions/types/constants that are used outside the module.
  • Provider-agnostic design: keep provider-agnostic abstractions and follow clean separation of concerns.
  • Comments and documentation: use as few inline comments as possible; behavior should be clear from tests and naming. When unifying/refactoring code, document the newly unified sections and the migration process with targeted comments and README updates.

Metadata

Metadata

Labels

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions