Dependency Management
In line with Happen's philosophy of simplicity, our approach to dependencies emphasizes direct runtime access with minimal framework abstractions.
Core Principles
Happen's dependency management follows three key principles:
Runtime Transparency: Your code has direct access to the runtime environment
Minimal System Dependencies: Only essential framework dependencies (primarily NATS) are injected at initialization
Event-Based Communication: Nodes interact through events, not traditional dependency injection
System-Level Dependencies
System dependencies are configured once during framework initialization:
// Initialize Happen with system-level dependencies
const happen = initializeHappen({
// NATS configuration as the primary dependency
nats: {
// Direct NATS client for server environments
server: {
servers: ['nats://localhost:4222'],
jetstream: true
},
// WebSocket client for browser environments
browser: {
servers: ['wss://localhost:8443'],
jetstream: true
}
},
// Optional: Override crypto implementation
crypto: cryptoImplementation
});
// The initialized framework provides the node creation function
const { createNode } = happen;These system dependencies represent the minimal set required for Happen to function across different runtime environments. By injecting them at initialization, we maintain runtime agnosticism while ensuring consistent behavior.
Direct Runtime Access
Rather than exposing a subset of features through abstraction layers, Happen encourages direct access to the runtime:
This approach provides several benefits:
Zero overhead: No performance penalty for accessing runtime features
Full capabilities: Access to everything the runtime offers, not just what the framework exposes
Runtime evolution: Immediate access to new runtime features without framework updates
Ecosystem compatibility: Works seamlessly with the broader ecosystem
Framework Agnosticism for Third-Party Libraries
When you need external libraries, import and use them directly:
Dependency Injection Through Closures
Use closures to create handlers with access to dependencies:
Flow State Through Closures
Although Happen provides a common object to share data across each flow you can also use closures to manage both dependencies and flow state, with explicit control over the flow through function returns:
How Flow Control Works With Closures
In this pattern, the flow is controlled through a clear mechanism:
Each flow step returns a function: When a handler wants to continue the flow, it returns a function that becomes the next handler.
Flow continues while functions are returned: The framework executes each returned function in sequence, as long as functions are being returned.
Flow ends when a non-function is returned: When a handler returns anything other than a function (like an object), the flow completes with that value as the result.
Dependencies and state flow through function parameters: Each step receives dependencies and state through its parameters, not through context.
Each step creates the next step with updated state: Functions create and return the next function in the chain, passing updated state and dependencies.
Benefits of Closure-Based Dependency Management
This closure-based approach offers several advantages:
Pure Functions: Flow handlers become pure functions with explicit dependencies
Clear Data Flow: Dependencies and state are explicitly passed between functions
Testability: Each flow step can be easily tested in isolation with mocked dependencies
Immutability: Encourages immutable state updates through function parameters
Separation of Concerns: Clearly separates framework concerns from application logic
NATS as the Primary Dependency
In the Happen framework, NATS serves as the primary dependency, providing core messaging, persistence, and coordination capabilities. This focused approach means:
Single Core Dependency: NATS is the primary external dependency you need to understand
Mature Ecosystem: NATS has clients for all major languages and platforms
Unified Capability Set: One system provides messaging, persistence, and coordination
Direct NATS Access: Your code can access NATS capabilities directly when needed
Dependency Management Philosophy
Happen's approach to dependencies reflects its broader philosophy: provide just enough framework to enable powerful capabilities, while getting out of the way and letting your code work directly with the runtime.
By limiting injected dependencies to only essential system needs (primarily NATS) and embracing direct runtime access and closure-based dependency management, Happen reduces complexity while maximizing flexibility.
There's no complex dependency injection system because, in most cases, you simply use JavaScript's natural closure mechanism to capture and pass dependencies where needed.
Last updated