Architecture
TerraDart is a thin authoring layer that produces standard Terraform JSON. The Dart side gives you types, refactor, and dart analyze; the Terraform side keeps state, planning, and apply unchanged.
Pipeline
From Dart to terraform apply
Author
Write final class XxxStack extends Stack using curated google_* factories from terradart_google.
Generate Terraform JSON
Call stack.writeTo('tf-out') to emit *.tf.json, or stack.synth() first if you need the in-memory SynthResult.
Apply
Run terraform plan and terraform apply against your existing state backend.
Hand off (optional)
Surface AppExport values as typed Dart constants and Terraform outputs for downstream apps.
What is synth?
Section titled “What is synth?”On the landing page, we say you generate *.tf.json — plain language for the outcome.
In the API, synth is the in-memory step: your Stack is walked and assembled into the Terraform JSON tree. Code: stack.synth() → SynthResult. Writing files is the next step: stack.writeTo(outDir).
Stack lifecycle (synth() internals)
Section titled “Stack lifecycle (synth() internals)”graph LR S[Stack instance] --> SY["synth()"] SY --> SR[SynthResult] SR --> W["writeTo(outDir)"] W --> TF[main.tf.json] W -.optional.-> AD[".app.dart constants"]
synth() is the pure, in-memory step. It walks the resources you registered with add(...), applies lifecycle wiring, dedups, and produces a SynthResult whose tfJson field carries the Terraform JSON tree.
writeTo(outDir) is the file-I/O wrapper. It always writes main.tf.json. If you registered AppExports via addExport(...) AND called setAppExportsOutputPath(...), it also writes a generated .app.dart file with typed Dart constants. If you registered exports but forgot to set the output path, writeTo throws StateError before writing anything — the failure mode is atomic, never partial.
Stack, Resource, and Data are abstract base class. Your subclasses must declare a class modifier:
final class AppInfraStack extends Stack { /* ... */ }base and sealed are also valid; what is rejected is plain class (or implements Stack, which would bypass the base-class state that synth depends on).
Provider integration
Section titled “Provider integration”terradart_google ships a curated subset of the HashiCorp google provider — typed factories for common services, expanded release by release. It does not wrap every resource block in the upstream provider yet. See status and the package README for what is available today; examples and the cookbook add fuller stacks over time.
TerraDart depends on the Google Terraform provider via two upstream sources, both reconciled automatically on a weekly cadence.
graph LR MM[Magic Modules repo] MM -->|MM generator| TPG[terraform-provider-google] MM -->|MM YAML overlay| MMY["mm/*.yaml fixtures"] TPG -->|"terraform providers schema -json"| SJ[schema.json fixture] SJ --> CG[terradart_codegen] MMY --> CG CG --> WR["terradart_google wrappers"]
The MM YAML overlay carries metadata that the provider schema does not surface — sensitive_fields, force_new, free-form description text. terradart_codegen merges both inputs into typed <resource>.schema.dart files that compose into the terradart_google package.
Provider tracking is pinned to GA ~> 7.0. The -beta provider is not yet curated; resources that only exist in beta are documented in tool/mm_yaml_sources.yaml with an upstream: null marker awaiting future opt-in.
A weekly GitHub Actions workflow (.github/workflows/schema-bump.yml) runs every Monday morning JST. It detects new terraform-provider-google v7 releases and MM YAML overlay changes, refreshes the local schema + MM fixtures, runs terradart wrap --check, and opens a single drift-report PR if anything needs maintainer attention. Quiet weeks produce no PR.
You don’t need to track this loop to use TerraDart. The reason it’s documented here is so that, when a v0.x.x release adds new curated factories or shifts an existing one, you can see exactly which upstream change drove it.
AppExport: the IaC ↔ application seam
Section titled “AppExport: the IaC ↔ application seam”AppExport is the typed handoff from your Stack to whatever Dart application code consumes the resources it provisions (for example Cloud Run functions handlers).
graph LR AE["addExport(name, AppExport)"] AE --> TFO["Terraform output block (main.tf.json)"] AE --> DC[".app.dart const class"] ST[Application code] --> DC TF[Terraform CLI] --> TFO
Two export kinds cover most cases:
ResourceIdExport— for values that aren’t known until apply time, like a Cloud Run service URL. PassemitTerraformOutput: trueand Terraform writes the resolved value into its output. Application code can fetch it viaterraform output -raw <name>.StringExport— for literals known at synth time (service name, region, project ID). These materialize asstatic constdeclarations on the generated<StackName>Exportsclass in your.app.dartfile —dart analyzecatches rename drift before you everterraform apply.
Calling setAppExportsOutputPath(...) is what triggers the .app.dart emission. Exports whose values are only known after apply (for example ResourceIdExport(topic.id)) emit Terraform output blocks only; literal-resolvable exports (for example ResourceIdExport(topic.nameRef)) become typed Dart constants. Runnable pattern: pubsub quickstart (lib/subscriber_stub.dart).
A worked end-to-end example lives in the cookbook single-project-app recipe, exporting coffee_service_uri (Terraform output), SERVICE_NAME, and REGION (Dart constants).
Non-goals
Section titled “Non-goals”- Not a Terraform replacement — state and apply stay in Terraform.
- Not multi-cloud yet — the Google provider only.
- Not a constructs framework in the pre-alpha cycle.
- Not module-block support — compose Terraform modules in HCL alongside TerraDart-generated
*.tf.json; both feed the sameterraform apply.
See README — Non-goals for the canonical list.
Further reading
Section titled “Further reading”terradart_coreon pub.dev — Stack / TfArg / AppExport API reference.terradart_googleon pub.dev — the curated factory catalog.terradart_codegenon pub.dev —wrap/codegenCLI reference.examples/on GitHub — runnable quickstart Stacks.