From dcc7edcd18d4186d84fa028e59a4b067ed113ac3 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Wed, 13 May 2026 18:04:34 +0000 Subject: [PATCH] refactor(marketing-pages): migrate binders to wireUseCase for both use cases Co-Authored-By: Claude Sonnet 4.6 --- .../marketing-pages/src/di/bind-dev-seed.ts | 166 ++++++++++++------ .../marketing-pages/src/di/bind-production.ts | 135 +++++++++----- 2 files changed, 198 insertions(+), 103 deletions(-) diff --git a/packages/marketing-pages/src/di/bind-dev-seed.ts b/packages/marketing-pages/src/di/bind-dev-seed.ts index 02290e5..7597ee6 100644 --- a/packages/marketing-pages/src/di/bind-dev-seed.ts +++ b/packages/marketing-pages/src/di/bind-dev-seed.ts @@ -6,7 +6,10 @@ import { type ILogger, } from "@repo/core-shared/instrumentation"; import type { BindContext } from "@repo/core-shared/di"; -import { assertFeatureConformance } from "@repo/core-shared/conformance"; +import { + assertFeatureConformance, + wireUseCase, +} from "@repo/core-shared/conformance"; import { marketingPagesManifest } from "../feature.manifest.js"; import { marketingPagesContainer } from "./container.js"; import { MARKETING_PAGES_SYMBOLS } from "./symbols.js"; @@ -20,7 +23,11 @@ import type { IPagesRepository } from "../application/repositories/pages.reposit import type { ISiteSettingsRepository } from "../application/repositories/site-settings.repository.interface.js"; import { userSignedUpEvent } from "@repo/auth"; import { onAuthUserSignedUpHandler } from "../events/handlers/on-auth-user-signed-up.handler.js"; -import { sendWelcomeEmailJob, type ISendWelcomeEmailJob, type SendWelcomeEmailInput } from "../jobs/send-welcome-email.job.js"; +import { + sendWelcomeEmailJob, + type ISendWelcomeEmailJob, + type SendWelcomeEmailInput, +} from "../jobs/send-welcome-email.job.js"; import { RecordingMailerService } from "../infrastructure/services/recording-mailer.service.js"; import type { IMailerService } from "../application/services/mailer.service.interface.js"; @@ -34,7 +41,9 @@ import type { IMailerService } from "../application/services/mailer.service.inte * Idempotent: safe to call multiple times; each call rebuilds fresh populated * repos and rebinds both symbols. */ -export async function bindDevSeedMarketingPages(ctx: BindContext): Promise { +export async function bindDevSeedMarketingPages( + ctx: BindContext, +): Promise { const { tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx; // Bind shared instrumentation into feature container @@ -44,11 +53,17 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise if (marketingPagesContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) { marketingPagesContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER); } - marketingPagesContainer.bind(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer); - marketingPagesContainer.bind(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger); + marketingPagesContainer + .bind(INSTRUMENTATION_SYMBOLS.TRACER) + .toConstantValue(tracer); + marketingPagesContainer + .bind(INSTRUMENTATION_SYMBOLS.LOGGER) + .toConstantValue(logger); // Pages repository — MockPagesRepository accepts an initial array. - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IPagesRepository)) { + if ( + marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IPagesRepository) + ) { marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IPagesRepository); } const pagesRepo = new MockPagesRepository(buildDevPages(), tracer, logger); @@ -57,8 +72,14 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise .toConstantValue(pagesRepo); // Site settings repository - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository)) { - marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository); + if ( + marketingPagesContainer.isBound( + MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository, + ) + ) { + marketingPagesContainer.unbind( + MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository, + ); } const settings = buildDevSiteSettings(); // Inline constant so dev seed returns exactly the seeded value (not the mock's default) @@ -66,43 +87,42 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise getSiteSettings: async () => settings, }; marketingPagesContainer - .bind(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository) + .bind( + MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository, + ) .toConstantValue(siteSettingsRepo); - // Wrap use cases + controllers identically to bind-production - const wrappedGetSiteSettings = withSpan( + // Use cases + const wrappedGetSiteSettings = wireUseCase({ + container: marketingPagesContainer, + symbol: MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase, + factory: getSiteSettingsUseCase, + deps: [siteSettingsRepo], + feature: "marketing-pages", + layer: "use-case", + name: "getSiteSettings", tracer, - { name: "marketing-pages.getSiteSettings", op: "use-case" }, - withCapture( - logger, - { feature: "marketing-pages", layer: "use-case", name: "marketing-pages.getSiteSettings" }, - getSiteSettingsUseCase(siteSettingsRepo), - ), - ); - const wrappedGetPageBySlug = withSpan( + logger, + }); + const wrappedGetPageBySlug = wireUseCase({ + container: marketingPagesContainer, + symbol: MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase, + factory: getPageBySlugUseCase, + deps: [pagesRepo], + feature: "marketing-pages", + layer: "use-case", + name: "getPageBySlug", tracer, - { name: "marketing-pages.getPageBySlug", op: "use-case" }, - withCapture( - logger, - { feature: "marketing-pages", layer: "use-case", name: "marketing-pages.getPageBySlug" }, - getPageBySlugUseCase(pagesRepo), - ), - ); + logger, + }); for (const sym of [ - MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase, - MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase, MARKETING_PAGES_SYMBOLS.IGetSiteSettingsController, MARKETING_PAGES_SYMBOLS.IGetPageBySlugController, ]) { - if (marketingPagesContainer.isBound(sym)) marketingPagesContainer.unbind(sym); + if (marketingPagesContainer.isBound(sym)) + marketingPagesContainer.unbind(sym); } - marketingPagesContainer - .bind(MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase) - .toConstantValue(wrappedGetSiteSettings); - marketingPagesContainer - .bind(MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase) - .toConstantValue(wrappedGetPageBySlug); marketingPagesContainer .bind(MARKETING_PAGES_SYMBOLS.IGetSiteSettingsController) @@ -112,7 +132,11 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise { name: "marketing-pages.getSiteSettings", op: "controller" }, withCapture( logger, - { feature: "marketing-pages", layer: "controller", name: "marketing-pages.getSiteSettings" }, + { + feature: "marketing-pages", + layer: "controller", + name: "marketing-pages.getSiteSettings", + }, getSiteSettingsController(wrappedGetSiteSettings), ), ), @@ -125,7 +149,11 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise { name: "marketing-pages.getPageBySlug", op: "controller" }, withCapture( logger, - { feature: "marketing-pages", layer: "controller", name: "marketing-pages.getPageBySlug" }, + { + feature: "marketing-pages", + layer: "controller", + name: "marketing-pages.getPageBySlug", + }, getPageBySlugController(wrappedGetPageBySlug), ), ), @@ -146,7 +174,10 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise if (queue) { const wrappedAuthUserSignedUp = withSpan( tracer, - { name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" }, + { + name: "marketing-pages.onAuthUserSignedUpHandler", + op: "event-handler", + }, withCapture( logger, { @@ -157,11 +188,23 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise onAuthUserSignedUpHandler(queue), ), ); - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) { - marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler); + if ( + marketingPagesContainer.isBound( + MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler, + ) + ) { + marketingPagesContainer.unbind( + MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler, + ); } - marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp); - bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp); + marketingPagesContainer + .bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler) + .toConstantValue(wrappedAuthUserSignedUp); + bus?.subscribe( + userSignedUpEvent, + "marketing-pages", + wrappedAuthUserSignedUp, + ); } // const wrappedSendWelcomeEmail = withSpan( @@ -177,23 +220,36 @@ export async function bindDevSeedMarketingPages(ctx: BindContext): Promise sendWelcomeEmailJob(mailer), ), ); - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob)) { - marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob); + if ( + marketingPagesContainer.isBound( + MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob, + ) + ) { + marketingPagesContainer.unbind( + MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob, + ); } - marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob).toConstantValue(wrappedSendWelcomeEmail); + marketingPagesContainer + .bind(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob) + .toConstantValue(wrappedSendWelcomeEmail); // Register the dev-seed in-memory queue handler so enqueue() actually fires // the wrapped job. Production binding skips this — the Payload task does it. - if (queue && "register" in queue && typeof (queue as { register?: unknown }).register === "function") { - (queue as { register: (slug: string, h: (input: unknown) => Promise) => void }).register( - "marketing-pages.send-welcome-email", - async (input) => { - const wrapped = marketingPagesContainer.get( - MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob, - ); - await wrapped(input as SendWelcomeEmailInput); - }, - ); + if ( + queue && + "register" in queue && + typeof (queue as { register?: unknown }).register === "function" + ) { + ( + queue as { + register: (slug: string, h: (input: unknown) => Promise) => void; + } + ).register("marketing-pages.send-welcome-email", async (input) => { + const wrapped = marketingPagesContainer.get( + MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob, + ); + await wrapped(input as SendWelcomeEmailInput); + }); } void realtime; void realtimeRegistry; diff --git a/packages/marketing-pages/src/di/bind-production.ts b/packages/marketing-pages/src/di/bind-production.ts index 71ae7b4..3667cd6 100644 --- a/packages/marketing-pages/src/di/bind-production.ts +++ b/packages/marketing-pages/src/di/bind-production.ts @@ -6,7 +6,10 @@ import { type ILogger, } from "@repo/core-shared/instrumentation"; import type { BindProductionContext } from "@repo/core-shared/di"; -import { assertFeatureConformance } from "@repo/core-shared/conformance"; +import { + assertFeatureConformance, + wireUseCase, +} from "@repo/core-shared/conformance"; import { marketingPagesContainer } from "./container"; import { MARKETING_PAGES_SYMBOLS } from "./symbols"; import { marketingPagesManifest } from "../feature.manifest"; @@ -23,7 +26,8 @@ import { RecordingMailerService } from "../infrastructure/services/recording-mai import type { IMailerService } from "../application/services/mailer.service.interface"; export function bindProductionMarketingPages(ctx: BindProductionContext): void { - const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx; + const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } = + ctx; // Bind shared instrumentation into feature container if (marketingPagesContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) { @@ -32,11 +36,17 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { if (marketingPagesContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) { marketingPagesContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER); } - marketingPagesContainer.bind(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer); - marketingPagesContainer.bind(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger); + marketingPagesContainer + .bind(INSTRUMENTATION_SYMBOLS.TRACER) + .toConstantValue(tracer); + marketingPagesContainer + .bind(INSTRUMENTATION_SYMBOLS.LOGGER) + .toConstantValue(logger); // Real repositories - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IPagesRepository)) { + if ( + marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IPagesRepository) + ) { marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IPagesRepository); } const pagesRepo = new PagesRepository(config, tracer, logger); @@ -44,53 +54,51 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { .bind(MARKETING_PAGES_SYMBOLS.IPagesRepository) .toConstantValue(pagesRepo); - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository)) { - marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository); + if ( + marketingPagesContainer.isBound( + MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository, + ) + ) { + marketingPagesContainer.unbind( + MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository, + ); } const siteSettingsRepo = new SiteSettingsRepository(config, tracer, logger); marketingPagesContainer .bind(MARKETING_PAGES_SYMBOLS.ISiteSettingsRepository) .toConstantValue(siteSettingsRepo); - // Use cases — wrapped with span + capture at bind time - const wrappedGetSiteSettings = withSpan( + // Use cases + const wrappedGetSiteSettings = wireUseCase({ + container: marketingPagesContainer, + symbol: MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase, + factory: getSiteSettingsUseCase, + deps: [siteSettingsRepo], + feature: "marketing-pages", + layer: "use-case", + name: "getSiteSettings", tracer, - { name: "marketing-pages.getSiteSettings", op: "use-case" }, - withCapture( - logger, - { feature: "marketing-pages", layer: "use-case", name: "marketing-pages.getSiteSettings" }, - getSiteSettingsUseCase(siteSettingsRepo), - ), - ); - const wrappedGetPageBySlug = withSpan( + logger, + }); + const wrappedGetPageBySlug = wireUseCase({ + container: marketingPagesContainer, + symbol: MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase, + factory: getPageBySlugUseCase, + deps: [pagesRepo], + feature: "marketing-pages", + layer: "use-case", + name: "getPageBySlug", tracer, - { name: "marketing-pages.getPageBySlug", op: "use-case" }, - withCapture( - logger, - { feature: "marketing-pages", layer: "use-case", name: "marketing-pages.getPageBySlug" }, - getPageBySlugUseCase(pagesRepo), - ), - ); - - for (const sym of [ - MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase, - MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase, - ]) { - if (marketingPagesContainer.isBound(sym)) marketingPagesContainer.unbind(sym); - } - marketingPagesContainer - .bind(MARKETING_PAGES_SYMBOLS.IGetSiteSettingsUseCase) - .toConstantValue(wrappedGetSiteSettings); - marketingPagesContainer - .bind(MARKETING_PAGES_SYMBOLS.IGetPageBySlugUseCase) - .toConstantValue(wrappedGetPageBySlug); + logger, + }); // Controllers — wrapped with span at bind time for (const sym of [ MARKETING_PAGES_SYMBOLS.IGetSiteSettingsController, MARKETING_PAGES_SYMBOLS.IGetPageBySlugController, ]) { - if (marketingPagesContainer.isBound(sym)) marketingPagesContainer.unbind(sym); + if (marketingPagesContainer.isBound(sym)) + marketingPagesContainer.unbind(sym); } marketingPagesContainer .bind(MARKETING_PAGES_SYMBOLS.IGetSiteSettingsController) @@ -100,7 +108,11 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { { name: "marketing-pages.getSiteSettings", op: "controller" }, withCapture( logger, - { feature: "marketing-pages", layer: "controller", name: "marketing-pages.getSiteSettings" }, + { + feature: "marketing-pages", + layer: "controller", + name: "marketing-pages.getSiteSettings", + }, getSiteSettingsController(wrappedGetSiteSettings), ), ), @@ -113,7 +125,11 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { { name: "marketing-pages.getPageBySlug", op: "controller" }, withCapture( logger, - { feature: "marketing-pages", layer: "controller", name: "marketing-pages.getPageBySlug" }, + { + feature: "marketing-pages", + layer: "controller", + name: "marketing-pages.getPageBySlug", + }, getPageBySlugController(wrappedGetPageBySlug), ), ), @@ -136,7 +152,10 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { if (queue) { const wrappedAuthUserSignedUp = withSpan( tracer, - { name: "marketing-pages.onAuthUserSignedUpHandler", op: "event-handler" }, + { + name: "marketing-pages.onAuthUserSignedUpHandler", + op: "event-handler", + }, withCapture( logger, { @@ -147,11 +166,23 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { onAuthUserSignedUpHandler(queue), ), ); - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler)) { - marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler); + if ( + marketingPagesContainer.isBound( + MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler, + ) + ) { + marketingPagesContainer.unbind( + MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler, + ); } - marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler).toConstantValue(wrappedAuthUserSignedUp); - bus?.subscribe(userSignedUpEvent, "marketing-pages", wrappedAuthUserSignedUp); + marketingPagesContainer + .bind(MARKETING_PAGES_SYMBOLS.IOnAuthUserSignedUpHandler) + .toConstantValue(wrappedAuthUserSignedUp); + bus?.subscribe( + userSignedUpEvent, + "marketing-pages", + wrappedAuthUserSignedUp, + ); } // const wrappedSendWelcomeEmail = withSpan( @@ -167,10 +198,18 @@ export function bindProductionMarketingPages(ctx: BindProductionContext): void { sendWelcomeEmailJob(mailer), ), ); - if (marketingPagesContainer.isBound(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob)) { - marketingPagesContainer.unbind(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob); + if ( + marketingPagesContainer.isBound( + MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob, + ) + ) { + marketingPagesContainer.unbind( + MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob, + ); } - marketingPagesContainer.bind(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob).toConstantValue(wrappedSendWelcomeEmail); + marketingPagesContainer + .bind(MARKETING_PAGES_SYMBOLS.ISendWelcomeEmailJob) + .toConstantValue(wrappedSendWelcomeEmail); void realtime; void realtimeRegistry; //