Skip to content

BlackBeltTechnology/pandino

Repository files navigation

Pandino: OSGi-Style Framework for TypeScript

npm version TypeScript License

A lightweight TypeScript framework that brings modular architecture to your applications. Build loosely-coupled, maintainable applications where different parts can communicate without knowing about each other directly.

Why Pandino?

Traditional DI vs Pandino Service Registry:

Traditional DI Pandino Service Registry
Static dependency injection Dynamic service discovery
Compile-time wiring Runtime service resolution
Hard-coded dependencies LDAP-filtered service selection
Single service per interface Multiple ranked services
Manual lifecycle management Automatic service lifecycle

Key Features

Feature What it Solves Benefit
πŸ”Œ Service Registry Hard-coded dependencies between modules Services discover each other dynamically
πŸ“¦ Bundle System Monolithic application architecture Modular containers with independent lifecycles
πŸ”„ Dynamic Dependencies Startup order dependencies Bundles start in any order, dependencies resolve automatically
πŸ“‘ Event System Tight coupling between modules Publish-subscribe messaging with topic-based routing
βš™οΈ Configuration Management Static application configuration Runtime configuration updates without restarts
πŸ—οΈ Declarative Services Complex service wiring boilerplate Decorator-based dependency injection
βš›οΈ React Integration Framework complexity in React apps Hook-based service discovery in components

πŸš€ Quick Concept Demo

import { Component, Service, Reference, Activate } from '@pandino/pandino';
import type { ComponentContext } from '@pandino/pandino';

// 1. Define service interfaces
interface UserService {
  findUser(id: string): User | null;
  createUser(data: UserData): User;
}

interface NotificationService {
  notify(message: string): void;
}

// 2. Create service implementations with SCR decorators
@Component({ name: 'user.service', immediate: true })
@Service({ interfaces: ['UserService'] })
class UserServiceImpl implements UserService {
  private users = new Map<string, User>();

  @Activate
  activate(context: ComponentContext): void {
    console.log('UserService activated');
  }

  findUser(id: string): User | null {
    return this.users.get(id) || null;
  }

  createUser(data: UserData): User {
    const user = new User(data);
    this.users.set(user.id, user);
    return user;
  }
}

@Component({ name: 'notification.service', immediate: true })
@Service({ interfaces: ['NotificationService'] })
class NotificationServiceImpl implements NotificationService {
  @Activate
  activate(): void {
    console.log('NotificationService activated');
  }

  notify(message: string): void {
    console.log(`πŸ“§ Notification: ${message}`);
  }
}

@Component({ name: 'order.service' })
@Service({ interfaces: ['OrderService'] })
class OrderService {
  // Services are injected automatically when available
  @Reference({ interface: 'UserService' })
  private userService?: UserService;

  @Reference({ interface: 'NotificationService' })
  private notificationService?: NotificationService;

  @Activate
  activate(): void {
    console.log('OrderService activated with dependencies');
  }

  async createOrder(userId: string, items: Item[]): Promise<Order> {
    // Dependencies resolved automatically - no imports needed!
    const user = this.userService?.findUser(userId);
    if (!user) throw new Error('User not found');

    const order = new Order(user, items);
    this.notificationService?.notify(`Order confirmed for ${user.email}`);
    return order;
  }
}

// 3. Register components with SCR
const scr = context.getService(context.getServiceReference('ServiceComponentRuntime')!);
const bundleId = context.getBundle().getBundleId();
await scr.registerComponent(UserServiceImpl, bundleId);
await scr.registerComponent(NotificationServiceImpl, bundleId);
await scr.registerComponent(OrderService, bundleId);

The Magic: All three services discover each other automatically. OrderService gets both dependencies injected without knowing how they're implemented!

Packages

Package Purpose Documentation
@pandino/pandino Core framework with service registry, bundles, and built-in services Core Documentation
@pandino/react-hooks React integration with hooks and components React Documentation

Architecture Concepts

Service-Oriented Architecture

Services are registered in a central registry and discovered by interface name and properties:

// Register with metadata
context.registerService('DatabaseService', new MySQLService(), {
  'db.type': 'mysql',
  'service.ranking': 100
});

// Discover by capabilities
const dbRefs = context.getServiceReferences('DatabaseService', '(db.type=mysql)');

Bundle Modularity

Bundles are self-contained modules with independent lifecycles:

// Each bundle manages its own services
const databaseBundle = {
  activator: {
    start(context) { /* register database services */ },
    stop(context) { /* cleanup */ }
  }
};

Dynamic Dependencies

Order doesn't matter - dependencies resolve when services become available:

await apiBundle.start();      // βœ… Starts immediately
await databaseBundle.start(); // βœ… API bundle automatically gets database service

Extender Pattern

A pattern where a dedicated bundle (extender) monitors other bundles and provides functionality based on their metadata:

β†’ Extender Pattern Documentation

Whiteboard Pattern

A service-oriented pattern that promotes loose coupling through a central service registry:

β†’ Whiteboard Pattern Documentation

Fragment Pattern

A pattern that allows a bundle (fragment) to attach to another bundle (host) and contribute its resources directly to the host:

β†’ Fragment Pattern Documentation

Use Cases

Scenario Traditional Approach Pandino Approach
Microservices Architecture Hard-coded service URLs Dynamic service discovery
Plugin Systems Manual plugin loading Bundle-based plugins with auto-discovery
Feature Flags Code-level toggles Service-level feature activation
Multi-tenant Apps Complex configuration management Service filtering by tenant properties
A/B Testing Conditional code blocks Multiple service implementations with ranking

Getting Started

Choose your integration approach:

Core Framework

npm install @pandino/pandino

β†’ Core Framework Guide

React Integration

npm install @pandino/pandino @pandino/react-hooks

β†’ React Integration Guide

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

Eclipse Public License - v 2.0

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •