import { withSpan, withCapture, INSTRUMENTATION_SYMBOLS, type ITracer, type ILogger, } from "@repo/core-shared/instrumentation"; import type { BindProductionContext } from "@repo/core-shared/di"; import { assertFeatureConformance } from "@repo/core-shared/conformance"; import { blogContainer } from "./container"; import { BLOG_SYMBOLS } from "./symbols"; import { blogManifest } from "../feature.manifest"; import { ArticlesRepository } from "../infrastructure/repositories/articles.repository"; import { getArticlesUseCase } from "../application/use-cases/get-articles.use-case"; import { getArticleBySlugUseCase } from "../application/use-cases/get-article-by-slug.use-case"; import { createArticleUseCase } from "../application/use-cases/create-article.use-case"; import { getArticlesController } from "../interface-adapters/controllers/get-articles.controller"; import { getArticleBySlugController } from "../interface-adapters/controllers/get-article-by-slug.controller"; import { createArticleController } from "../interface-adapters/controllers/create-article.controller"; export function bindProductionBlog(ctx: BindProductionContext): void { const { config, tracer, logger, bus, queue, realtime, realtimeRegistry } = ctx; // Bind shared instrumentation into feature container if (blogContainer.isBound(INSTRUMENTATION_SYMBOLS.TRACER)) { blogContainer.unbind(INSTRUMENTATION_SYMBOLS.TRACER); } if (blogContainer.isBound(INSTRUMENTATION_SYMBOLS.LOGGER)) { blogContainer.unbind(INSTRUMENTATION_SYMBOLS.LOGGER); } blogContainer.bind(INSTRUMENTATION_SYMBOLS.TRACER).toConstantValue(tracer); blogContainer.bind(INSTRUMENTATION_SYMBOLS.LOGGER).toConstantValue(logger); // Real repository if (blogContainer.isBound(BLOG_SYMBOLS.IArticlesRepository)) { blogContainer.unbind(BLOG_SYMBOLS.IArticlesRepository); } const repo = new ArticlesRepository(config, tracer, logger); blogContainer.bind(BLOG_SYMBOLS.IArticlesRepository).toConstantValue(repo); // Use cases — wrapped with span + capture at bind time (R41, R44) const wrappedGetArticles = withSpan( tracer, { name: "blog.getArticles", op: "use-case" }, withCapture( logger, { feature: "blog", layer: "use-case", name: "blog.getArticles" }, getArticlesUseCase(repo), ), ); const wrappedGetArticleBySlug = withSpan( tracer, { name: "blog.getArticleBySlug", op: "use-case" }, withCapture( logger, { feature: "blog", layer: "use-case", name: "blog.getArticleBySlug" }, getArticleBySlugUseCase(repo), ), ); const wrappedCreateArticle = withSpan( tracer, { name: "blog.createArticle", op: "use-case" }, withCapture( logger, { feature: "blog", layer: "use-case", name: "blog.createArticle" }, createArticleUseCase(repo), ), ); if (blogContainer.isBound(BLOG_SYMBOLS.IGetArticlesUseCase)) { blogContainer.unbind(BLOG_SYMBOLS.IGetArticlesUseCase); } if (blogContainer.isBound(BLOG_SYMBOLS.IGetArticleBySlugUseCase)) { blogContainer.unbind(BLOG_SYMBOLS.IGetArticleBySlugUseCase); } if (blogContainer.isBound(BLOG_SYMBOLS.ICreateArticleUseCase)) { blogContainer.unbind(BLOG_SYMBOLS.ICreateArticleUseCase); } blogContainer.bind(BLOG_SYMBOLS.IGetArticlesUseCase).toConstantValue(wrappedGetArticles); blogContainer .bind(BLOG_SYMBOLS.IGetArticleBySlugUseCase) .toConstantValue(wrappedGetArticleBySlug); blogContainer.bind(BLOG_SYMBOLS.ICreateArticleUseCase).toConstantValue(wrappedCreateArticle); // Controllers — wrapped with span at bind time if (blogContainer.isBound(BLOG_SYMBOLS.IGetArticlesController)) { blogContainer.unbind(BLOG_SYMBOLS.IGetArticlesController); } if (blogContainer.isBound(BLOG_SYMBOLS.IGetArticleBySlugController)) { blogContainer.unbind(BLOG_SYMBOLS.IGetArticleBySlugController); } if (blogContainer.isBound(BLOG_SYMBOLS.ICreateArticleController)) { blogContainer.unbind(BLOG_SYMBOLS.ICreateArticleController); } blogContainer .bind(BLOG_SYMBOLS.IGetArticlesController) .toConstantValue( withSpan( tracer, { name: "blog.getArticles", op: "controller" }, withCapture( logger, { feature: "blog", layer: "controller", name: "blog.getArticles" }, getArticlesController(wrappedGetArticles), ), ), ); blogContainer .bind(BLOG_SYMBOLS.IGetArticleBySlugController) .toConstantValue( withSpan( tracer, { name: "blog.getArticleBySlug", op: "controller" }, withCapture( logger, { feature: "blog", layer: "controller", name: "blog.getArticleBySlug" }, getArticleBySlugController(wrappedGetArticleBySlug), ), ), ); blogContainer .bind(BLOG_SYMBOLS.ICreateArticleController) .toConstantValue( withSpan( tracer, { name: "blog.createArticle", op: "controller" }, withCapture( logger, { feature: "blog", layer: "controller", name: "blog.createArticle" }, createArticleController(wrappedCreateArticle), ), ), ); // bus + queue are accept-and-forward in Phase 6; consumed by Phase 7 generator // output at the / anchors below. void bus; void queue; void realtime; void realtimeRegistry; // // // // Boot-time conformance check. assertFeatureConformance( blogContainer, blogManifest, { getArticles: BLOG_SYMBOLS.IGetArticlesUseCase, getArticleBySlug: BLOG_SYMBOLS.IGetArticleBySlugUseCase, createArticle: BLOG_SYMBOLS.ICreateArticleUseCase, }, ctx, ); }