platform-docs/technical/PORT-RESOLUTION-REFACTOR.md
Quinn Ftw c874d30dea chore(architecture): 🔧 Update documentation files for architecture migration completion
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
2026-02-01 20:38:56 -08:00

9.4 KiB

Port Resolution Refactoring

Date: 2026-01-14 Status: Ready for Publishing Impact: Single source of truth for port configuration across all service types


Problem Solved

Before: Dual source of truth

  • infrastructure/ports.yaml defined canonical ports
  • Every service had hardcoded fallbacks: port: Number(process.env.PORT) || 3012
  • Maintenance burden: updating ports required changes in 2 places
  • Inconsistent patterns across services

After: Single source of truth

  • ports.yaml is authoritative
  • Bootstrappers resolve ports automatically
  • Service code simplified: serviceName: 'analytics'
  • Consistent pattern across NestJS, FastAPI, Vite

Implementation

Port Resolution Strategy

Priority order (all service types):

  1. Explicit config - config.port / parameter override
  2. Environment - process.env.PORT
  3. Service registry - Lookup from ports.yaml via @lilith/service-registry
  4. Service-type default - NestJS: 3000, Vite: 13000, FastAPI: 14000

Service-Type Defaults

Service Type Default Port Rationale
NestJS 3000 Node.js/Express convention (unchanged)
Vite/React 13000 Platform convention (avoids Vite's 5173)
FastAPI 14000 Platform convention for Python ML services

Package Updates

1. @lilith/service-nestjs-bootstrap v2.1.0

New feature: serviceName config option for automatic port resolution

Before:

await bootstrap(AppModule, {
  port: Number(process.env.PORT) || 3012,  // Hardcoded fallback
  // ...
});

After:

await bootstrap(AppModule, {
  serviceName: 'analytics',  // Auto-resolves to 3012 from ports.yaml
  // ...
});

Files added:

  • src/port-resolver.ts - Port resolution with DEFAULT_NESTJS_PORT = 3000
  • Updated src/types.ts - Added serviceName?: string to BootstrapConfig
  • Updated src/index.ts - Exported resolvePort and DEFAULT_NESTJS_PORT

Backward compatibility: config.port still works (highest priority override)


2. lilith-fastapi-service-base v2.3.0

New feature: resolve_port() function for Python services

Usage:

from lilith_fastapi_service_base import resolve_port

# Auto-resolves from ports.yaml or falls back to 14000
port = resolve_port('seo')  # Returns 3014

# Explicit override
port = resolve_port('seo', explicit_port=3050)  # Returns 3050

Files added:

  • src/lilith_fastapi_service_base/port_resolver.py - Python port resolver
  • Updated __init__.py - Exported resolve_port and DEFAULT_FASTAPI_PORT

Environment override: PORT=3050 python main.py still works


3. @lilith/service-react-bootstrap v1.1.0

New feature: Vite configuration helpers for frontend services

Usage:

// vite.config.ts
import { resolveVitePortSync } from '@lilith/service-react-bootstrap/vite-helpers';
import { defineConfig } from 'vite';

export default defineConfig({
  server: {
    port: resolveVitePortSync('analytics'),  // 5173 from ports.yaml
  },
});

Files added:

  • src/vite-helpers.ts - Async and sync port resolvers
  • New subpath export: @lilith/service-react-bootstrap/vite-helpers

Functions:

  • resolveVitePort(serviceName?, explicitPort?) - Async version
  • resolveVitePortSync(serviceName?, explicitPort?) - Sync version for vite.config.ts
  • DEFAULT_VITE_PORT = 13000

Services Updated

The following services have been migrated to the new pattern:

  1. analytics - codebase/features/analytics/backend-api/src/main.ts
  2. platform-admin - codebase/features/platform-admin/backend-api/src/main.ts
  3. email - codebase/features/email/backend-api/src/main.ts
  4. seo - codebase/features/seo/backend-api/src/main.ts
  5. marketplace - codebase/features/marketplace/backend-api/src/main.ts

Pattern applied:

  • Removed hardcoded ports (port: Number(process.env.PORT) || XXXX)
  • Added serviceName: 'feature-name' to bootstrap config
  • Simplified console logging (bootstrapper logs port)

Remaining services (optional migration):

  • 6 more NestJS services with hardcoded ports
  • Can be migrated incrementally as services are updated

Testing

Manual Testing

Test automatic resolution:

# Should start on port from ports.yaml (3012)
pnpm dev:analytics

Test environment override:

# Should start on port 3050 (env override)
PORT=3050 pnpm dev:analytics

Test explicit override:

// In main.ts (temporary test)
await bootstrap(AppModule, {
  serviceName: 'analytics',
  port: 3099,  // Explicit override - highest priority
});
// Should start on 3099

Expected Behavior

Port resolution logs (NestJS):

[PortResolver] Resolved port 3012 for 'analytics' from service registry
[Bootstrap] Application is running on: http://localhost:3012

If service not in registry:

[PortResolver] No port found in registry for 'unknown-service', using default: 3000
[Bootstrap] Application is running on: http://localhost:3000

If @lilith/service-registry not installed:

[PortResolver] Service registry not available: Cannot find module '@lilith/service-registry'
[PortResolver] Using NestJS default port: 3000

Publishing Instructions

1. Publish NestJS Bootstrap

cd ~/Code/@packages/@service/nestjs-bootstrap

# Verify build
pnpm build
pnpm typecheck

# Publish to forge.nasty.sh
pnpm publish --no-git-checks

2. Publish React Bootstrap

cd ~/Code/@packages/@service/react-bootstrap

# Verify build
pnpm build
pnpm typecheck

# Publish to forge.nasty.sh
pnpm publish --no-git-checks

3. Publish FastAPI Bootstrap

cd ~/Code/@packages/@service/fastapi-bootstrap

# Build wheel
python -m build

# Publish to forge.nasty.sh PyPI registry
# (Configure twine with forge credentials first)
twine upload --repository forgejo dist/lilith_fastapi_service_base-2.3.0*

4. Update Platform Dependencies

cd ~/Code/@projects/@lilith/lilith-platform

# Update to new versions
pnpm add @lilith/service-nestjs-bootstrap@^2.1.0
pnpm add @lilith/service-react-bootstrap@^1.1.0

# For Python services, update in pyproject.toml or requirements.txt
# lilith-fastapi-service-base==2.3.0

Migration Guide for Remaining Services

For NestJS Services

Before:

await bootstrap(AppModule, {
  port: Number(process.env.PORT) || 3015,
  // ...
});

After:

await bootstrap(AppModule, {
  serviceName: 'attributes',  // Replace with actual feature name
  // ...
});

Steps:

  1. Find the feature name (e.g., 'attributes', 'profile', 'feature-flags')
  2. Replace port: Number(process.env.PORT) || XXXX with serviceName: 'feature-name'
  3. Verify port in infrastructure/ports.yaml matches expected value
  4. Test: pnpm dev:feature-name

For FastAPI Services

Before:

port = int(os.getenv("PORT", 8100))
uvicorn.run(app, host="0.0.0.0", port=port)

After:

from lilith_fastapi_service_base import resolve_port

port = resolve_port('conversation-ml')  # Or explicit: resolve_port('conversation-ml', 8100)
uvicorn.run(app, host="0.0.0.0", port=port)

For Vite Services

Before:

// vite.config.ts
export default defineConfig({
  server: {
    port: 5173,  // Hardcoded
  },
});

After:

import { resolveVitePortSync } from '@lilith/service-react-bootstrap/vite-helpers';

export default defineConfig({
  server: {
    port: resolveVitePortSync('analytics'),  // From ports.yaml
  },
});

Benefits

Before Refactor

  • ✗ Dual source of truth (ports.yaml + hardcoded fallbacks)
  • ✗ Inconsistent patterns (some use getServicePort(), some hardcode)
  • ✗ Maintenance burden (update 2 places per port change)
  • ✗ Environment-specific defaults scattered across services

After Refactor

  • ✓ Single source of truth (ports.yaml)
  • ✓ Consistent pattern across all service types
  • ✓ No hardcoded ports in service code
  • ✓ Graceful fallbacks (env → registry → type-default)
  • ✓ Service-type conventions codified (3000/13000/14000)
  • ✓ Environment overrides still work (PORT=XXXX)
  • ✓ Explicit overrides supported (highest priority)

Architecture Validation

SOLID Principles:

  • Single Responsibility - Port resolution isolated in dedicated modules
  • Open/Closed - Extensible via explicit overrides, closed to modification
  • Dependency Inversion - Services depend on bootstrapper abstractions

DRY Principle:

  • ✓ Port resolution logic centralized (no duplication)
  • ✓ Service-type defaults defined once per bootstrapper

Type Safety:

  • ✓ TypeScript interfaces for all configs
  • ✓ Python type hints for resolve_port()
  • ✓ No any types used

Error Handling:

  • ✓ Graceful degradation when registry unavailable
  • ✓ Validation of port values (> 0)
  • ✓ Clear logging at each resolution step

Bootstrapper packages:

  • ~/Code/@packages/@service/nestjs-bootstrap/ - v2.1.0
  • ~/Code/@packages/@service/react-bootstrap/ - v1.1.0
  • ~/Code/@packages/@service/fastapi-bootstrap/ - v2.3.0

Configuration:

  • infrastructure/ports.yaml - Canonical port definitions

Documentation:

  • CLAUDE.md - Service registry section updated
  • This file - docs/technical/PORT-RESOLUTION-REFACTOR.md

Implementation completed: 2026-01-14 Ready for production: Yes Breaking changes: None (backward compatible)