DRFT Architecture
This document describes the high-level architecture of DRFT and how its components interact.
System Overview
DRFT follows a modular architecture with clear separation of concerns:
┌─────────────────────────────────────────────────────────┐
│ DRFT Core │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Parser │ │ Planner │ │ Executor │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ State │ │ Graph │ │ Events │ │
│ │ Manager │ │ Builder │ │ System │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
│
│ Provider Interface
│
┌───────────────┼───────────────┐
│ │ │
┌───────▼──────┐ ┌──────▼──────┐ ┌──────▼──────┐
│ AWS Provider │ │ GCP Provider│ │ K8s Provider│
└──────────────┘ └─────────────┘ └─────────────┘
1. Stack
A Stack is the top-level container that holds:
- Provider configurations
- Resource definitions
- State management
- Execution context
class DrftStack {
final String name;
final List<Provider> providers;
final List<Resource> resources;
final StateManager stateManager;
Future<Plan> plan();
Future<ApplyResult> apply(Plan plan);
Future<State> refresh();
}
2. Resources
Resources are immutable Dart classes that represent the desired state of resources. Each resource:
- Has a unique identifier
- Has final properties that define its desired state
- Can depend on other resources (as Resource references)
- Is generic over a ResourceState type for type safety
abstract class Resource<StateType extends ResourceState> {
final String id;
final List<Resource> dependencies;
const Resource({
required this.id,
this.dependencies = const [],
});
}
See also: Resource, ResourceState, and Provider Relationship for detailed explanation of how Resources relate to ResourceStates and Providers.
3. Providers
Providers are responsible for:
- Translating Resource definitions to API calls
- Managing resources in the actual system
- Creating ResourceState instances after operations
- Reporting current state
- Handling provider-specific logic
abstract class Provider {
String get name;
String get version;
Future<ResourceState> createResource(Resource resource);
Future<ResourceState> readResource(String resourceId, String resourceType);
Future<ResourceState> updateResource(
ResourceState current,
Resource desired,
);
Future<void> deleteResource(ResourceState state);
bool canHandle(Resource resource);
}
See also: Resource, ResourceState, and Provider Relationship for detailed explanation of how Providers transform Resources into ResourceStates.
4. State Manager
The state manager:
- Persists resource state to disk
- Loads previous state
- Compares desired vs actual state
- Tracks resource metadata
class StateManager {
final String stateFilePath;
Future<State> load();
Future<void> save(State state);
StateDiff diff(State desired, State actual);
}
5. Planner
The planner:
- Compares desired state with actual state
- Identifies changes (create, update, delete)
- Builds dependency graph
- Orders operations correctly
- Validates changes
class Planner {
Plan createPlan({
required State desired,
required State actual,
required DependencyGraph graph,
});
}
6. Executor
The executor:
- Executes planned operations in order
- Handles rollback on errors
- Reports progress
- Updates state after operations
class Executor {
Future<ApplyResult> execute(
Plan plan,
StateManager stateManager,
List<Provider> providers,
);
}
Planning Phase
1. Load desired state from Dart code
└─> Parse resources and providers
2. Load actual state from state file
└─> Read previous state.json
3. Build dependency graph
└─> Analyze resource dependencies
4. Compare states
└─> Identify create/update/delete operations
5. Generate plan
└─> Ordered list of operations
Execution Phase
1. Validate plan
└─> Check for errors, conflicts
2. Execute operations in order
└─> For each operation:
├─> Find appropriate provider
├─> Execute operation
├─> Update state
└─> Emit events
3. Save final state
└─> Persist to state file
4. Return result
└─> Summary of changes
State File Format
State files are JSON documents that track:
{
"version": "1.0",
"stack": "my-infrastructure",
"resources": {
"vm.web-server": {
"id": "i-1234567890abcdef0",
"type": "aws.ec2.instance",
"properties": {
"name": "web-server",
"image": "ubuntu-22.04",
"size": "medium"
},
"created": "2024-01-15T10:30:00Z",
"updated": "2024-01-15T10:30:00Z"
}
},
"metadata": {
"lastApplied": "2024-01-15T10:30:00Z",
"drftVersion": "0.1.0"
}
}
Dependency Resolution
DRFT automatically resolves dependencies:
-
Explicit Dependencies: Resources can declare dependencies
final db = Database(name: 'app-db') ..dependsOn = [network.id]; -
Implicit Dependencies: DRFT detects when one resource references another
final vm = VirtualMachine( name: 'web-server', networkId: network.id, // Implicit dependency ); -
Graph Building: Creates a directed acyclic graph (DAG)
-
Topological Sort: Orders operations to satisfy dependencies