Domain Layer¶
The domain layer contains business logic and domain entities extracted from the execution engine. This layer provides clean abstractions for tasks, parameters, and project configuration with clear separation of concerns.
Package Structure¶
internal/domain/
├── task/
│ ├── task.go # Task entity with operations
│ ├── registry.go # Task registration and lookup
│ └── dependencies.go # Dependency resolution logic
├── parameter/
│ ├── parameter.go # Parameter entity
│ └── validation.go # Parameter validation & constraints
└── project/
├── project.go # Project entity
└── settings.go # Settings management
Domain Entities¶
Task Domain¶
The task domain manages task entities, their registration, and dependency resolution:
- Task: Represents a drun task with metadata, parameters, dependencies, and body
- Registry: Thread-safe task registration and lookup with namespace support
- DependencyResolver: Handles circular dependency detection and topological sorting
Parameter Domain¶
The parameter domain provides parameter validation with advanced constraints:
- Parameter: Parameter entity with type information and constraints
- Validator: Validates parameter values against data types, patterns, and ranges
Project Domain¶
The project domain manages project-level configuration:
- Project: Project entity with settings, shell configs, and lifecycle hooks
- SettingsManager: Manages project settings with defaults
Design Principles¶
- Domain-Driven Design: Business logic separated from infrastructure
- Single Responsibility: Each package has one clear purpose
- Testability: Domain logic can be tested in isolation
- Thread Safety: Registry provides concurrent access support
- Type Safety: Strong typing with validation
Usage¶
The domain layer is ready for integration with the engine. Currently, it exists as standalone packages that can be used independently or integrated into the execution flow.
Example: Task Registry¶
import "github.com/phillarmonic/drun/v2/internal/domain/task"
// Create registry
registry := task.NewRegistry()
// Register task
domainTask := task.NewTask(astTask, "namespace", "source.drun")
err := registry.Register(domainTask)
// Lookup task
foundTask, err := registry.Get("taskName")
// List all tasks
allTasks := registry.List()
Example: Parameter Validation¶
import "github.com/phillarmonic/drun/v2/internal/domain/parameter"
// Create validator
validator := parameter.NewValidator()
// Define parameter with constraints
param := ¶meter.Parameter{
Name: "port",
DataType: "number",
MinValue: &minVal, // 1
MaxValue: &maxVal, // 65535
}
// Validate value
value := types.NewValue(types.NumberType, "8080")
err := validator.Validate(param, value)
Engine Integration¶
The domain layer is fully integrated with the execution engine:
Task Execution:
- Engine uses
task.Registryfor task registration and lookup task.DependencyResolverprovides dependency ordering- Tasks execute using domain statements, not AST nodes
Parameter Validation:
parameter.Validatorvalidates all parameter values- Supports advanced constraints (patterns, ranges, enums)
- Type-safe parameter handling throughout execution
Project Management:
project.Projectmanages project-level configuration- Lifecycle hooks use domain statements
- Project settings integration
Domain Statement System¶
The statement package (internal/domain/statement/) provides:
Statement Types:
- Core: Action, Shell, Variable, Parameter
- Control: Conditional, Loop, Try/Catch, Break, Continue
- Operations: File, Docker, Git, HTTP, Network, Download
- Advanced: Detection, TaskCall, TaskFromTemplate, UseSnippet
Converter:
FromAST()- Unidirectional converter from AST to domain (one-way only)- All execution uses pure domain types (no AST conversion during runtime)
Benefits:
- Clean separation from parser internals
- Type-safe domain entities
- Easier testing and validation
- Future-proof for engine improvements
Created: October 2025 Status: Complete & Integrated Usage: Production (all examples passing)