refactor(blog): factory-style use cases + per-use-case controllers + getArticleBySlug
- Use cases (create-article, get-articles, get-article-by-slug NEW) → factory functions - Controllers split: articles.controller.ts → 3 single-responsibility files - DI module wires factories with .toDynamicValue() - tRPC router resolves controllers via container Refactor log: §2, §3, §4.1, §4.2, §5.1 Spec: §6.2 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -22,15 +22,18 @@ describe("blogContainer", () => {
|
||||
expect(repo).toBeInstanceOf(MockArticlesRepository);
|
||||
});
|
||||
|
||||
it("supports rebinding to a custom repo", () => {
|
||||
blogContainer.unbind(BLOG_SYMBOLS.IArticlesRepository);
|
||||
const custom = new MockArticlesRepository();
|
||||
blogContainer
|
||||
.bind<IArticlesRepository>(BLOG_SYMBOLS.IArticlesRepository)
|
||||
.toConstantValue(custom);
|
||||
const resolved = blogContainer.get<IArticlesRepository>(
|
||||
BLOG_SYMBOLS.IArticlesRepository,
|
||||
);
|
||||
expect(resolved).toBe(custom);
|
||||
it("resolves IGetArticlesController from the container", () => {
|
||||
const ctrl = blogContainer.get(BLOG_SYMBOLS.IGetArticlesController);
|
||||
expect(typeof ctrl).toBe("function");
|
||||
});
|
||||
|
||||
it("resolves ICreateArticleController from the container", () => {
|
||||
const ctrl = blogContainer.get(BLOG_SYMBOLS.ICreateArticleController);
|
||||
expect(typeof ctrl).toBe("function");
|
||||
});
|
||||
|
||||
it("resolves IGetArticleBySlugController from the container", () => {
|
||||
const ctrl = blogContainer.get(BLOG_SYMBOLS.IGetArticleBySlugController);
|
||||
expect(typeof ctrl).toBe("function");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,10 +2,68 @@ import { ContainerModule, type interfaces } from "inversify";
|
||||
|
||||
import type { IArticlesRepository } from "../application/repositories/articles.repository.interface";
|
||||
import { MockArticlesRepository } from "../infrastructure/repositories/articles.repository.mock";
|
||||
import {
|
||||
getArticlesUseCase,
|
||||
type IGetArticlesUseCase,
|
||||
} from "../application/use-cases/get-articles.use-case";
|
||||
import {
|
||||
createArticleUseCase,
|
||||
type ICreateArticleUseCase,
|
||||
} from "../application/use-cases/create-article.use-case";
|
||||
import {
|
||||
getArticleBySlugUseCase,
|
||||
type IGetArticleBySlugUseCase,
|
||||
} from "../application/use-cases/get-article-by-slug.use-case";
|
||||
import {
|
||||
getArticlesController,
|
||||
type IGetArticlesController,
|
||||
} from "../interface-adapters/controllers/get-articles.controller";
|
||||
import {
|
||||
createArticleController,
|
||||
type ICreateArticleController,
|
||||
} from "../interface-adapters/controllers/create-article.controller";
|
||||
import {
|
||||
getArticleBySlugController,
|
||||
type IGetArticleBySlugController,
|
||||
} from "../interface-adapters/controllers/get-article-by-slug.controller";
|
||||
import { BLOG_SYMBOLS } from "./symbols";
|
||||
|
||||
export const BlogModule = new ContainerModule((bind: interfaces.Bind) => {
|
||||
bind<IArticlesRepository>(BLOG_SYMBOLS.IArticlesRepository).to(
|
||||
MockArticlesRepository,
|
||||
bind<IArticlesRepository>(BLOG_SYMBOLS.IArticlesRepository).to(MockArticlesRepository);
|
||||
|
||||
bind<IGetArticlesUseCase>(BLOG_SYMBOLS.IGetArticlesUseCase).toDynamicValue((ctx) =>
|
||||
getArticlesUseCase(
|
||||
ctx.container.get<IArticlesRepository>(BLOG_SYMBOLS.IArticlesRepository),
|
||||
),
|
||||
);
|
||||
|
||||
bind<ICreateArticleUseCase>(BLOG_SYMBOLS.ICreateArticleUseCase).toDynamicValue((ctx) =>
|
||||
createArticleUseCase(
|
||||
ctx.container.get<IArticlesRepository>(BLOG_SYMBOLS.IArticlesRepository),
|
||||
),
|
||||
);
|
||||
|
||||
bind<IGetArticleBySlugUseCase>(BLOG_SYMBOLS.IGetArticleBySlugUseCase).toDynamicValue((ctx) =>
|
||||
getArticleBySlugUseCase(
|
||||
ctx.container.get<IArticlesRepository>(BLOG_SYMBOLS.IArticlesRepository),
|
||||
),
|
||||
);
|
||||
|
||||
bind<IGetArticlesController>(BLOG_SYMBOLS.IGetArticlesController).toDynamicValue((ctx) =>
|
||||
getArticlesController(
|
||||
ctx.container.get<IGetArticlesUseCase>(BLOG_SYMBOLS.IGetArticlesUseCase),
|
||||
),
|
||||
);
|
||||
|
||||
bind<ICreateArticleController>(BLOG_SYMBOLS.ICreateArticleController).toDynamicValue((ctx) =>
|
||||
createArticleController(
|
||||
ctx.container.get<ICreateArticleUseCase>(BLOG_SYMBOLS.ICreateArticleUseCase),
|
||||
),
|
||||
);
|
||||
|
||||
bind<IGetArticleBySlugController>(BLOG_SYMBOLS.IGetArticleBySlugController).toDynamicValue((ctx) =>
|
||||
getArticleBySlugController(
|
||||
ctx.container.get<IGetArticleBySlugUseCase>(BLOG_SYMBOLS.IGetArticleBySlugUseCase),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
export const BLOG_SYMBOLS = {
|
||||
IArticlesRepository: Symbol.for("blog:IArticlesRepository"),
|
||||
// Use cases
|
||||
IGetArticlesUseCase: Symbol.for("blog:IGetArticlesUseCase"),
|
||||
ICreateArticleUseCase: Symbol.for("blog:ICreateArticleUseCase"),
|
||||
IGetArticleBySlugUseCase: Symbol.for("blog:IGetArticleBySlugUseCase"),
|
||||
// Controllers
|
||||
IGetArticlesController: Symbol.for("blog:IGetArticlesController"),
|
||||
ICreateArticleController: Symbol.for("blog:ICreateArticleController"),
|
||||
IGetArticleBySlugController: Symbol.for("blog:IGetArticleBySlugController"),
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user