Skip to content

Latest commit

 

History

History
274 lines (222 loc) · 6.88 KB

File metadata and controls

274 lines (222 loc) · 6.88 KB

Framework Adaptation Guide

How to apply the auto-discovery pattern to different tech stacks.


Node.js + Express + Sequelize

The primary pattern. See SKILL.md for complete implementation.

Key files to refactor:

  • src/models/index.js → Directory scan
  • src/models/model-associations.js → associations/ loader
  • server/loaders/services.js → service-registrations/ loader
  • src/routes/index.js → *.routes.js scan

Node.js + Express + Mongoose

// src/models/index.js — Mongoose auto-discovery
const fs = require('fs');
const path = require('path');
const mongoose = require('mongoose');

const models = {};
const SKIP = [path.basename(__filename)];

fs.readdirSync(__dirname)
  .filter(f => f.endsWith('.model.js') && !SKIP.includes(f))
  .sort()
  .forEach(file => {
    try {
      const model = require(path.join(__dirname, file));
      // Mongoose models register themselves; just track them
      const name = path.basename(file, '.model.js');
      models[name] = model;
    } catch (e) {
      console.error(`Model load failed ${file}:`, e.message);
    }
  });

module.exports = models;

Mongoose doesn't need associations file — references are defined within schemas. But middleware/plugins can be auto-discovered:

// src/models/plugins/ — Auto-discovered Mongoose plugins
fs.readdirSync(pluginsDir)
  .filter(f => f.endsWith('.plugin.js'))
  .forEach(file => {
    const plugin = require(path.join(pluginsDir, file));
    mongoose.plugin(plugin);
  });

Python + Django

Django already has excellent feature isolation via its app system.

Django's built-in auto-discovery:

  • Models: Automatic via INSTALLED_APPS
  • Admin: admin.autodiscover()
  • URLs: include() in urlpatterns

What to add for parallel AI dev:

# Each feature is a Django app
# feature_a/
#   models.py
#   views.py
#   urls.py
#   signals.py  ← cross-model relationships
#   services.py ← business logic

# settings.py — just add the app name
INSTALLED_APPS = [
    # ... core apps ...
    'features.chatflow',      # Feature A
    'features.web_widget',    # Feature B
    'features.instagram_dm',  # Feature C
]

Auto-discover feature URLs:

# urls.py — auto-scan feature apps
import importlib
import pkgutil

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('core.urls')),
]

# Auto-discover feature URLs
features_package = importlib.import_module('features')
for _, name, _ in pkgutil.iter_modules(features_package.__path__):
    try:
        urlpatterns.append(
            path(f'api/{name}/', include(f'features.{name}.urls'))
        )
    except ModuleNotFoundError:
        pass  # Feature doesn't have URLs

AI agent instruction for Django:

FILES YOU CREATE:
✅ features/{feature}/models.py
✅ features/{feature}/views.py
✅ features/{feature}/urls.py
✅ features/{feature}/services.py
✅ features/{feature}/signals.py
✅ features/{feature}/tests.py
✅ features/{feature}/migrations/

FILES YOU NEVER EDIT:
❌ settings.py (INSTALLED_APPS)  ← only the orchestrator adds this
❌ core/urls.py
❌ Any other feature's directory

Python + Flask + SQLAlchemy

# app/models/__init__.py — Auto-discovery
import importlib
import pkgutil
from app.extensions import db

def register_models():
    """Auto-discover and import all model modules."""
    package = importlib.import_module('app.models')
    for _, name, _ in pkgutil.iter_modules(package.__path__):
        if name != '__init__':
            importlib.import_module(f'app.models.{name}')

# app/blueprints/__init__.py — Auto-discover blueprints
def register_blueprints(app):
    """Auto-discover and register all feature blueprints."""
    import os
    blueprint_dir = os.path.dirname(__file__)
    for item in sorted(os.listdir(blueprint_dir)):
        module_path = os.path.join(blueprint_dir, item)
        if os.path.isdir(module_path) and not item.startswith('_'):
            try:
                mod = importlib.import_module(f'app.blueprints.{item}')
                if hasattr(mod, 'bp'):
                    app.register_blueprint(mod.bp, url_prefix=f'/api/{item}')
            except Exception as e:
                print(f"Failed to load blueprint {item}: {e}")

Feature structure:

app/blueprints/
├── chatflow/
│   ├── __init__.py  ← defines bp = Blueprint('chatflow', ...)
│   ├── routes.py
│   └── services.py
├── web_widget/
│   ├── __init__.py
│   ├── routes.py
│   └── services.py

TypeScript + NestJS

NestJS is already modular by design. Each feature is a module.

// app.module.ts — Auto-discover feature modules
import { Module } from '@nestjs/common';
import * as glob from 'glob';

// Auto-discover all *.module.ts in features/
const featureModules = glob
  .sync('features/*/*.module.ts', { cwd: __dirname })
  .map(file => require(`./${file}`))
  .flatMap(mod => Object.values(mod))
  .filter(mod => typeof mod === 'function');

@Module({
  imports: [
    CoreModule,
    DatabaseModule,
    ...featureModules,  // Auto-discovered
  ],
})
export class AppModule {}

Each feature is fully self-contained:

src/features/
├── chatflow/
│   ├── chatflow.module.ts
│   ├── chatflow.controller.ts
│   ├── chatflow.service.ts
│   └── entities/
│       └── chatflow.entity.ts
├── web-widget/
│   ├── web-widget.module.ts
│   ├── web-widget.controller.ts
│   └── web-widget.service.ts

Go + Gin/Echo

// internal/features/registry.go — Auto-registration pattern
package features

import "github.com/gin-gonic/gin"

// FeatureRegistrar is implemented by each feature
type FeatureRegistrar interface {
    RegisterRoutes(r *gin.RouterGroup)
    RegisterServices(container *ServiceContainer)
}

// registry holds all feature registrars
var registry []FeatureRegistrar

// Register adds a feature (called in each feature's init())
func Register(f FeatureRegistrar) {
    registry = append(registry, f)
}

// InitAll registers all features with the app
func InitAll(r *gin.Engine, container *ServiceContainer) {
    api := r.Group("/api")
    for _, f := range registry {
        f.RegisterRoutes(api)
        f.RegisterServices(container)
    }
}
// internal/features/chatflow/init.go
package chatflow

func init() {
    features.Register(&ChatFlowFeature{})
}

Summary: Universal Pattern

Regardless of language/framework, the principle is the same:

Component Pattern
Models Auto-discover files in a directory
Relationships Each feature registers its own in a separate file
Services Plugin-style registration via directory scan
Routes Auto-discover route files or blueprints
Tests Per-feature test files, integration runner scans all
Migrations Additive (CREATE/ALTER), sorted by filename