Claude-skill-registry-data migrate-service-to-container

Guides migration from old_code/ to containers/ following SERVICE_MIGRATION_GUIDE.md. Transforms imports to use @coder/shared, adds tenantId to database queries, replaces hardcoded URLs with config, updates routes with auth/tenant enforcement, sets up event publishers/consumers, and transforms service communication. Use when migrating existing services, transforming old code patterns, or refactoring legacy code to new architecture.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry-data
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry-data "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/migrate-service-to-container" ~/.claude/skills/majiayu000-claude-skill-registry-data-migrate-service-to-container && rm -rf "$T"
manifest: data/migrate-service-to-container/SKILL.md
source content

Migrate Service to Container

Guides migration from

old_code/
to
containers/
following SERVICE_MIGRATION_GUIDE.md and ModuleImplementationGuide.md standards.

Pre-Migration Analysis

Before migrating, analyze:

  • Dependencies: What services does this depend on?
  • Database: What Cosmos DB containers does it use?
  • Events: What events does it publish/consume?
  • External APIs: What external services does it call?
  • Configuration: What configuration values does it need?
  • Routes: What API endpoints does it expose?
  • Business Logic: What are the core services/classes?

Migration Steps

Step 1: Create Module Structure

Use

create-container-module
skill or manually create:

mkdir -p containers/<service-name>/{config,src/{config,routes,services,types,utils,events,middleware},tests/{unit,integration}}

Step 2: Transform Imports

Old Pattern:

import { config } from '../config/env.js';
import { CosmosClient } from '@azure/cosmos';

New Pattern:

import { CosmosDBClient } from '@coder/shared';
import { loadConfig } from '../config';

Reference: ModuleImplementationGuide.md Section 5 (Dependency Rules)

Step 3: Transform Database Queries

Old Pattern:

async getData(id: string) {
  const query = `SELECT * FROM c WHERE c.id = @id`;
  // ❌ No tenantId
}

New Pattern:

async getData(tenantId: string, id: string) {
  const container = this.db.getContainer('service_data');
  const query = `SELECT * FROM c WHERE c.tenantId = @tenantId AND c.id = @id`;
  const parameters = [
    { name: '@tenantId', value: tenantId },
    { name: '@id', value: id }
  ];
  // ✅ tenantId required, uses shared client
}

Reference: SERVICE_MIGRATION_GUIDE.md Step 6, ModuleImplementationGuide.md Section 8

Step 4: Replace Hardcoded URLs

Old Pattern:

const response = await fetch('http://localhost:3021/api/users/123');

New Pattern:

import { ServiceClient } from '@coder/shared';

const client = new ServiceClient({
  baseUrl: config.services.auth.url, // From config
  timeout: 5000,
});

const response = await client.get('/api/v1/users/123', {
  headers: {
    'X-Tenant-ID': tenantId,
    'Authorization': `Bearer ${serviceToken}`,
  },
});

Reference: SERVICE_MIGRATION_GUIDE.md Step 7, ModuleImplementationGuide.md Section 5.3

Step 5: Transform Routes

Old Pattern:

fastify.get('/api/my-service/data', async (request, reply) => {
  const service = new MyService();
  const data = await service.getData(request.params.id);
  return reply.send(data);
});

New Pattern:

import { authenticateRequest, tenantEnforcementMiddleware } from '@coder/shared';

fastify.get<{ Params: { id: string } }>(
  '/api/v1/data/:id',
  {
    preHandler: [authenticateRequest(), tenantEnforcementMiddleware()],
  },
  async (request, reply) => {
    // ✅ tenantId available from tenantEnforcementMiddleware
    const tenantId = request.user!.tenantId;
    const data = await service.getData(tenantId, request.params.id);
    return reply.send({ data });
  }
);

Reference: SERVICE_MIGRATION_GUIDE.md Step 5

Step 6: Update Error Handling

Old Pattern:

throw new Error('Something went wrong');

New Pattern:

import { AppError } from '@coder/shared';
throw new AppError('Something went wrong', 400, 'BAD_REQUEST');

Reference: ModuleImplementationGuide.md Section 10 (Error Handling)

Step 7: Set Up Event Publishing

New Pattern:

import { EventPublisher } from '@coder/shared';

const publisher = new EventPublisher(config.rabbitmq);
await publisher.publish('service.resource.created', {
  id: resource.id,
  tenantId: tenantId,
  timestamp: new Date().toISOString(),
});

Reference: ModuleImplementationGuide.md Section 9 (Event-Driven Communication)

Step 8: Transform Service Initialization

Old Pattern:

const service = new MyService(cosmosClient, redis, monitoring);

New Pattern:

import { getDatabaseClient, getCacheClient } from '@coder/shared';
const db = getDatabaseClient();
const cache = getCacheClient();
const service = new MyService(db, cache);

Migration Checklist

Pre-Migration

  • Analyze dependencies
  • Map database containers
  • Identify events (published/consumed)
  • List API endpoints
  • Document configuration needs

Code Migration

  • Create module directory structure
  • Copy service files
  • Transform imports (use @coder/shared)
  • Add tenantId to all database queries
  • Replace hardcoded URLs with config
  • Transform routes (add auth, tenant enforcement)
  • Update error handling (use AppError)
  • Add event publishing/consuming

Configuration

  • Create config/default.yaml
  • Create config/schema.json
  • Create config/index.ts loader
  • Add environment variable documentation

Validation

  • No hardcoded ports/URLs
  • All queries include tenantId
  • Service-to-service auth implemented
  • Follows ModuleImplementationGuide.md