The core philosophy of Demeter-DI is rooted in two distinct technical goals: adhering to the Law of Demeter (LoD) and providing a fluent, domain-specific language (DSL) for dependency management using the dsl-framework.

Why the Shift?

In our early technical explorations, we encountered significant bottlenecks when dealing with project scale. The shift to a pure Node.js architecture allowed us to implement:

  • Asynchronous FS traversal: For non-blocking dependency resolution.
  • Regex Buffering: To handle replacements and resolutions in memory before I/O.
  • Atomic Operations: Ensuring that container builds don't leave the application in a partial state.

The DSL Framework: The Engine Room

The real power behind Demeter-DI isn't just the injection; it's the dsl-framework. This allows developers to chain methods like .define(), .compose(), and .create() into a readable, expressive narrative.

By using Proxy objects, we've removed the need to manually invoke service functions with parentheses when accessing them from the container.

Core Implementation Patterns

1. Define: Constants and Parameters

The define method is procedural and functional. It isolates constants from logic, promoting readability.

const container = containerFactoryFactory
    .define('PI', 3.14)
    .define('API_URL', '[https://api.example.com](https://api.example.com)')();

2. Compose: Lazy Singletons

compose creates a small DSL within your container. It utilizes Lazy Initialization—the service is executed only once, the first time it is accessed.

// The service is evaluated only when container.myService is called the first time.
containerFactoryFactory.compose('myService', (dep1, dep2) => dep1 + dep2, ['dep1', 'dep2']);

3. Create: The Factory Pattern

Unlike compose, create evaluates every time it is called. This is essential for resource-heavy operations or scenarios where a fresh instance is required (e.g., test fixtures).

// Useful for overriding complex services in test environments
containerFactoryFactory.create('complexService', () => ['fixture', 'data']);

Architectural Benefits: The Law of Demeter

Demeter-DI enforces the principle that an object should only interact with its immediate neighbors. By managing dependencies through a container, you decouple the "how" of service creation from the "what" of service usage.

  • Loose Coupling: Services don't need to know how to instantiate their dependencies.
  • Testability: Redefining a service with .create() allows for instant mocking without touching the business logic.
  • Atomic Consistency: The closing function call () triggers the final container build, ensuring all links are resolved.

Conclusion

By unifying the fluent interface of the dsl-framework with a strict DI container, we've built a tool that values developer ergonomics as much as architectural purity. It is built for a JS/TS world where clarity and performance must coexist.