Service Discovery
When to use
- User wants to know what to provision in dispatch layer for a service
- User asks "what services/modules/resources does this project have?"
- Before running ring:adding-multi-tenancy on a new service
- User asks about MongoDB indexes in a project
Skip when
- Not a Go project
- Task does not involve service discovery, dispatch layer, or resource mapping
- Project has no external dependencies
Related
Complementary: ring:adding-multi-tenancy, ring:implementing-tasks
Prerequisites
- Go project with go.mod in the current working directory
Scans Go project to produce dispatch layer registration data. Orchestrator executes all detection phases directly.
Phase 1: Service Detection
bash# Service name grep "ApplicationName\|ServiceName" internal/bootstrap/config.go 2>/dev/null | head -5 cat .env.example 2>/dev/null | grep -i "APPLICATION_NAME\|SERVICE_NAME" | head -3 # Service type test -f go.mod && cat go.mod | head -3 # module path hints service purpose ls internal/adapters/ 2>/dev/null # adapters reveal type # Unified service check ls components/ 2>/dev/null # multiple components = unified service
Output:
service_name: "my-service" is_unified: true | false components: [{name, path, applicationName}] # if unified
Phase 2: Module Detection
bash# Strategy A: Explicit WithModule calls (preferred) grep -rn "WithModule(" internal/ components/ 2>/dev/null # Extract string arg: WithModule("onboarding") → module "onboarding" # Strategy B: Component-based (if no WithModule found) ls components/ # each component = one module # module_name = component's ApplicationName # Strategy C: Single-component fallback # module_name = service ApplicationName
Merge: Strategy A → B fills gaps → C fallback.
Phase 3: Resource Detection per Module
For each module, scan {component_path}/internal/adapters/:
bash# PostgreSQL: subdirectory existence ls {base_path}postgres/ 2>/dev/null # MongoDB ls {base_path}mongodb/ 2>/dev/null || ls {base_path}mongo/ 2>/dev/null # RabbitMQ ls {base_path}rabbitmq/ 2>/dev/null grep -l "producer\|Producer" {base_path}rabbitmq/ 2>/dev/null grep -l "consumer\|Consumer" {base_path}rabbitmq/ 2>/dev/null # Redis (informational only — NOT a dispatch layer resource) ls {base_path}redis/ 2>/dev/null
Phase 3.5: Database Name Detection per Module
bash# From bootstrap config grep -E 'env:"POSTGRES_NAME|env:"DB_.*_NAME|env:"MONGO_NAME|env:"MONGO_.*_NAME' \ {component_path}/internal/bootstrap/config.go # From .env.example (actual values) grep -E "POSTGRES_NAME=|DB_.*_NAME=|MONGO_NAME=|MONGO_.*_NAME=" {component_path}/.env.example # External datasources grep -E "DATASOURCE_.*_DATABASE=" {component_path}/.env.example
Cross-reference across modules: same database name in 2+ modules = shared (provision once).
Phase 4: MongoDB Index Detection & Migration File Generation
Only execute if MongoDB was detected in any module during Phase 3.
Execute the procedure in references/mongodb-index-detection.md — Steps 1, 2, 3, 4 only.
(Detection and local generation only; S3 upload is handled in Phase 6.)
- Step 1 — Detect in-code index definitions (
EnsureIndexes,IndexModel,CreateIndex) per module. - Step 2 — Detect existing local migration files in
{component_path}/scripts/mongodb/*.up.json+*.down.json(fallback legacy*.js). LOCAL ONLY — no S3 lookup. - Step 3 — Cross-reference code vs. local migration files, classify each as
covered/missing_migration/migration_only. - Step 4 — Generate one
.up.json+.down.jsonfile pair per missing index (atomic per index, NOT grouped by collection):- Path:
{component_path}/scripts/mongodb/{NNNNNN}_{index_name}.up.json/.down.json— per-module directory preserves ownership for Phase 6 upload (single-component services resolve{component_path}to repo root) - Naming:
idx_{collection}_{fields}(oruniq_*for uniqueness business rules) - HARD GATE: every
.up.jsonMUST have explicit"options.name"matching the file name - Track per module: populate
module.generated_migration_files = [{up_file, down_file, index_name}, ...]so Phase 6 knows exactly which files belong to which module
- Path:
Format reminder: the dispatch layer reads .up.json / .down.json from S3 and applies indexes on tenant provisioning. The service does NOT execute these files. Legacy .js scripts are NOT uploaded — only JSON migrations.
Phase 4.5: PostgreSQL Migration Detection
Only execute if PostgreSQL was detected in any module during Phase 3.
Detection only — Postgres migrations are written by developers; this skill does NOT generate .sql files.
bash# Common golang-migrate locations (per module path resolved in Phase 3) ls {component_path}/scripts/postgres/*.up.sql 2>/dev/null ls {component_path}/scripts/postgresql/*.up.sql 2>/dev/null ls {component_path}/db/migrations/*.up.sql 2>/dev/null ls {component_path}/migrations/*.up.sql 2>/dev/null
For each .up.sql file found:
- Verify the matching
.down.sqlexists (golang-migrate convention). - Map it to its module (by directory path).
- Track for the Phase 6 upload.
Store: module.postgres_migrations = [{up_file, down_file, sequence, description}]
If a .up.sql exists without .down.sql → flag in Phase 5 HTML report (golang-migrate requires pairs).
Phase 5: Generate HTML Report
Dispatch ring:visualizing:
Generate HTML report showing: - Service: {service_name} | Unified: {is_unified} - For each module: - Resources table: type, repositories/collections, has_producer, has_consumer, queues - Database names: postgres_db, mongo_db (with env var names) - Redis: detected / not detected (note: key prefixing only) - Shared databases: list with modules that share them - Dispatch layer registration template (JSON) - MongoDB index coverage table (collection / keys / code / migration / index name) per `references/mongodb-index-detection.md` "Report Section: MongoDB Index Coverage" - PostgreSQL migration table per module: file count, missing-pair warnings (`.up.sql` without `.down.sql`) - Upload-ready summary: total Mongo pairs and Postgres pairs to be offered for upload in Phase 6 Style: clean, data-dense table layout
Phase 6: Optional S3 Upload
Execute after Phase 5. Always opt-in — never upload without explicit user confirmation.
text1. Print summary and ask: "Ready to upload to S3: - MongoDB: {N} index migration pairs (.up.json/.down.json) across {M} modules - PostgreSQL: {P} migration pairs (.up.sql/.down.sql) across {Q} modules Upload to S3? (y/n)" 2. If user declines → done. Files remain local. Report status: "Upload skipped by user." 3. If user accepts: a. Verify AWS CLI: `aws --version`. If absent → abort, report "AWS CLI not installed." b. Ask: "Which S3 bucket? (e.g., lerian-development-migrations)" c. Ask: "Which environment? (staging / production)" d. Verify access: `aws s3 ls s3://{bucket}/{env}/ 2>&1`. If access denied or 404 → abort, report error. 4. Upload Mongo files (per module, best-effort — continue on individual failures): for each module with module.generated_migration_files populated in Phase 4: for each {up_file, down_file} in module.generated_migration_files: aws s3 cp {up_file} s3://{bucket}/{env}/{service}/{module}/mongodb/$(basename {up_file}) --content-type "application/json" aws s3 cp {down_file} s3://{bucket}/{env}/{service}/{module}/mongodb/$(basename {down_file}) --content-type "application/json" 5. Upload Postgres files (per module, best-effort): for each module with module.postgres_migrations populated in Phase 4.5: for each {up_file, down_file} in module.postgres_migrations: aws s3 cp {up_file} s3://{bucket}/{env}/{service}/{module}/postgresql/$(basename {up_file}) --content-type "application/sql" aws s3 cp {down_file} s3://{bucket}/{env}/{service}/{module}/postgresql/$(basename {down_file}) --content-type "application/sql" 6. Verify per module: aws s3 ls s3://{bucket}/{env}/{service}/{module}/mongodb/ aws s3 ls s3://{bucket}/{env}/{service}/{module}/postgresql/ 7. Report uploaded files (full s3:// paths) and any errors. Do NOT abort the whole run if a single file fails — list failures at the end.
Path convention (matches actual bucket layout): s3://{bucket}/{env}/{service}/{module}/{mongodb|postgresql}/{filename}
Output: Dispatch Layer Registration Template
json{ "service": "{service_name}", "modules": [ { "name": "{module_name}", "resources": [ { "type": "postgresql", "database": "{db_name}", "env_var": "POSTGRES_NAME" }, { "type": "mongodb", "database": "{db_name}", "env_var": "MONGO_NAME" }, { "type": "rabbitmq", "has_producer": true, "has_consumer": true, "queues": ["{queue_name}"] } ] } ], "shared_databases": [] }

