You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Overview: There are two independent merchant modules in the backend, each with its own controller, service, DTOs, entities, and specs, serving overlapping merchant-profile functionality on two different URL prefixes. src/merchant/ declares @Controller("merchants") and src/merchants/ declares @Controller("merchant/profile"). Both are registered in AppModule. A client updating a merchant profile has two endpoints to choose from, backed by two code paths that can and will diverge.
Details:
src/merchant/merchant.controller.ts is @Controller("merchants") and exposes GET /merchants/profile, PATCH /merchants/settings, GET /merchants/checklist, PATCH /merchants/checklist, and PATCH /merchants/checklist/sync.
src/merchants/merchants.controller.ts is @Controller("merchant/profile") and exposes GET /merchant/profile, PUT /merchant/profile, and PATCH /merchant/profile.
So the same conceptual resource is reachable at both /merchants/profile and /merchant/profile, differing by one character, backed by MerchantService and MerchantsService respectively. The singular and plural prefixes are inverted between the two modules, which makes the collision easy to miss in review and easy to hit by mistake in a client.
Each module carries its own dto/ and entities/ directory, so the merchant shape is declared twice. A field added to one profile DTO does not appear in the other, and validation rules can differ per endpoint for the same underlying record.
MerchantRolesGuard is applied per route in both controllers, so authorisation is configured twice for the same resource, with no guarantee the two stay aligned.
Both are registered in AppModule as MerchantsModule and MerchantModule, one line apart, which is exactly the kind of near-identical import pairing that survives review indefinitely.
Web and mobile currently each hand-maintain merchant service clients, so a divergence between the two backend paths surfaces as a runtime inconsistency in one client and not the other.
Scope:
Determine which module is authoritative and which routes each client actually calls today, including web, mobile, and any external consumer.
Consolidate onto a single merchant module with one service, one DTO set, and one entity definition covering profile, settings, and checklist.
Settle on one URL prefix, and keep the retired path working as an explicit deprecated alias for a stated period rather than removing it silently.
Reconcile the two DTOs into one, resolving any field or validation differences deliberately rather than by whichever module wins.
Apply the merchant role guard once, consistently, across the consolidated surface.
Update the web and mobile merchant service clients to the retained path.
src/merchant/declares@Controller("merchants")andsrc/merchants/declares@Controller("merchant/profile"). Both are registered inAppModule. A client updating a merchant profile has two endpoints to choose from, backed by two code paths that can and will diverge.src/merchant/merchant.controller.tsis@Controller("merchants")and exposesGET /merchants/profile,PATCH /merchants/settings,GET /merchants/checklist,PATCH /merchants/checklist, andPATCH /merchants/checklist/sync.src/merchants/merchants.controller.tsis@Controller("merchant/profile")and exposesGET /merchant/profile,PUT /merchant/profile, andPATCH /merchant/profile./merchants/profileand/merchant/profile, differing by one character, backed byMerchantServiceandMerchantsServicerespectively. The singular and plural prefixes are inverted between the two modules, which makes the collision easy to miss in review and easy to hit by mistake in a client.dto/andentities/directory, so the merchant shape is declared twice. A field added to one profile DTO does not appear in the other, and validation rules can differ per endpoint for the same underlying record.MerchantRolesGuardis applied per route in both controllers, so authorisation is configured twice for the same resource, with no guarantee the two stay aligned.AppModuleasMerchantsModuleandMerchantModule, one line apart, which is exactly the kind of near-identical import pairing that survives review indefinitely.SorobanServiceclasses in Consolidate the duplicated SorobanService and the split ADMIN_SECRET_KEY/SOROBAN_SECRET_KEY configuration #459. With no published API contract (Bootstrap SwaggerModule and publish an OpenAPI contract for the backend API #458), neither the split nor a subsequent divergence is visible to client authors.backend/src/merchant/backend/src/merchants/backend/src/app.module.tsbackend/src/common/guards/merchant-roles.guard.tsweb/lib/merchant-service.tsmobile/lib/merchant-service.ts