From c5fab7d61a2da3b2a3d6b9024a59440b2c6ca6ba Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Tue, 14 Oct 2025 11:53:57 +0800 Subject: [PATCH] refactor: dependency injection to use a centralized Container class for managing application state and services --- frontend/src/app.d.ts | 4 +- frontend/src/hooks.server.ts | 29 +--- .../lib/auth/adapter/presenter/authBloc.ts | 10 +- frontend/src/lib/container.ts | 134 ++++++++++++++++++ .../src/lib/home/framework/ui/Motto.svelte | 2 +- .../framework/ui/ImageManagementPage.svelte | 2 +- .../framework/ui/UploadImageDialoag.svelte | 4 +- frontend/src/routes/+layout.svelte | 5 + frontend/src/routes/dashboard/+layout.svelte | 17 +-- frontend/src/routes/post/+page.server.ts | 3 +- frontend/src/routes/post/+page.svelte | 18 +-- frontend/src/routes/post/[id]/+page.server.ts | 3 +- frontend/src/routes/post/[id]/+page.svelte | 16 +-- 13 files changed, 166 insertions(+), 81 deletions(-) create mode 100644 frontend/src/lib/container.ts diff --git a/frontend/src/app.d.ts b/frontend/src/app.d.ts index 3d0dcb6..eb3f9c6 100644 --- a/frontend/src/app.d.ts +++ b/frontend/src/app.d.ts @@ -5,9 +5,7 @@ declare global { // interface Error {} interface Locals { - authBloc: import('$lib/auth/adapter/presenter/authBloc').AuthBloc; - postListBloc: import('$lib/post/adapter/presenter/postListBloc').PostListBloc; - postBloc: import('$lib/post/adapter/presenter/postBloc').PostBloc; + container: import('$lib/container').Container; } // interface PageData {} diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index e6675b7..e1e1b4e 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -1,21 +1,8 @@ import { sequence } from '@sveltejs/kit/hooks'; import * as Sentry from '@sentry/sveltekit'; -import { PostRepositoryImpl } from '$lib/post/adapter/gateway/postRepositoryImpl'; -import { PostBloc } from '$lib/post/adapter/presenter/postBloc'; -import { PostListBloc } from '$lib/post/adapter/presenter/postListBloc'; -import { GetAllPostsUseCase } from '$lib/post/application/useCase/getAllPostsUseCase'; -import { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase'; -import { PostApiServiceImpl } from '$lib/post/framework/api/postApiServiceImpl'; import type { Handle } from '@sveltejs/kit'; import { Environment } from '$lib/environment'; -import { AuthApiServiceImpl } from '$lib/auth/framework/api/authApiServiceImpl'; -import { AuthRepositoryImpl } from '$lib/auth/adapter/gateway/authRepositoryImpl'; -import type { AuthApiService } from '$lib/auth/adapter/gateway/authApiService'; -import type { AuthRepository } from '$lib/auth/application/gateway/authRepository'; -import type { PostApiService } from '$lib/post/adapter/gateway/postApiService'; -import type { PostRepository } from '$lib/post/application/gateway/postRepository'; -import { AuthBloc } from '$lib/auth/adapter/presenter/authBloc'; -import { GetCurrentUserUseCase } from '$lib/auth/application/useCase/getCurrentUserUseCase'; +import { Container } from '$lib/container'; Sentry.init({ dsn: Environment.SENTRY_DSN, @@ -24,18 +11,8 @@ Sentry.init({ }); export const handle: Handle = sequence(Sentry.sentryHandle(), ({ event, resolve }) => { - const authApiService: AuthApiService = new AuthApiServiceImpl(event.fetch); - const authRepository: AuthRepository = new AuthRepositoryImpl(authApiService); - const getCurrentUserUseCase = new GetCurrentUserUseCase(authRepository); - - const postApiService: PostApiService = new PostApiServiceImpl(event.fetch); - const postRepository: PostRepository = new PostRepositoryImpl(postApiService); - const getAllPostsUseCase = new GetAllPostsUseCase(postRepository); - const getPostUseCase = new GetPostUseCase(postRepository); - - event.locals.authBloc = new AuthBloc(getCurrentUserUseCase); - event.locals.postListBloc = new PostListBloc(getAllPostsUseCase); - event.locals.postBloc = new PostBloc(getPostUseCase); + const container = new Container(event.fetch); + event.locals.container = container; return resolve(event); }); diff --git a/frontend/src/lib/auth/adapter/presenter/authBloc.ts b/frontend/src/lib/auth/adapter/presenter/authBloc.ts index a793cf3..2a962a4 100644 --- a/frontend/src/lib/auth/adapter/presenter/authBloc.ts +++ b/frontend/src/lib/auth/adapter/presenter/authBloc.ts @@ -12,15 +12,7 @@ export class AuthBloc { status: StatusType.Idle, }); - constructor( - private readonly getCurrentUserUseCase: GetCurrentUserUseCase, - initialData?: AuthViewModel - ) { - this.state.set({ - status: StatusType.Idle, - data: initialData, - }); - } + constructor(private readonly getCurrentUserUseCase: GetCurrentUserUseCase) {} get subscribe() { return this.state.subscribe; diff --git a/frontend/src/lib/container.ts b/frontend/src/lib/container.ts new file mode 100644 index 0000000..10a7785 --- /dev/null +++ b/frontend/src/lib/container.ts @@ -0,0 +1,134 @@ +import type { AuthApiService } from '$lib/auth/adapter/gateway/authApiService'; +import { AuthRepositoryImpl } from '$lib/auth/adapter/gateway/authRepositoryImpl'; +import { AuthBloc } from '$lib/auth/adapter/presenter/authBloc'; +import type { AuthRepository } from '$lib/auth/application/gateway/authRepository'; +import { GetCurrentUserUseCase } from '$lib/auth/application/useCase/getCurrentUserUseCase'; +import { AuthApiServiceImpl } from '$lib/auth/framework/api/authApiServiceImpl'; +import type { ImageApiService } from '$lib/image/adapter/gateway/imageApiService'; +import { ImageRepositoryImpl } from '$lib/image/adapter/gateway/imageRepositoryImpl'; +import { ImageBloc } from '$lib/image/adapter/presenter/imageBloc'; +import type { ImageRepository } from '$lib/image/application/gateway/imageRepository'; +import { UploadImageUseCase } from '$lib/image/application/useCase/uploadImageUseCase'; +import { ImageApiServiceImpl } from '$lib/image/framework/api/imageApiServiceImpl'; +import type { PostApiService } from '$lib/post/adapter/gateway/postApiService'; +import { PostRepositoryImpl } from '$lib/post/adapter/gateway/postRepositoryImpl'; +import { PostBloc } from '$lib/post/adapter/presenter/postBloc'; +import type { PostInfoViewModel } from '$lib/post/adapter/presenter/postInfoViewModel'; +import { PostListBloc } from '$lib/post/adapter/presenter/postListBloc'; +import type { PostViewModel } from '$lib/post/adapter/presenter/postViewModel'; +import type { PostRepository } from '$lib/post/application/gateway/postRepository'; +import { GetAllPostsUseCase } from '$lib/post/application/useCase/getAllPostsUseCase'; +import { GetPostUseCase } from '$lib/post/application/useCase/getPostUseCase'; +import { PostApiServiceImpl } from '$lib/post/framework/api/postApiServiceImpl'; + +export class Container { + private useCases: UseCases; + + constructor(fetchFn: typeof fetch) { + const apiServices = new ApiServices(fetchFn); + const repositories = new Repositories(apiServices); + this.useCases = new UseCases(repositories); + } + + createAuthBloc(): AuthBloc { + return new AuthBloc(this.useCases.getCurrentUserUseCase); + } + + createImageBloc(): ImageBloc { + return new ImageBloc(this.useCases.uploadImageUseCase); + } + + createPostListBloc(initialData?: readonly PostInfoViewModel[]): PostListBloc { + return new PostListBloc(this.useCases.getAllPostsUseCase, initialData); + } + + createPostBloc(initialData?: PostViewModel): PostBloc { + return new PostBloc(this.useCases.getPostUseCase, initialData); + } +} + +class ApiServices { + private fetchFn: typeof fetch; + + private _authApiService?: AuthApiService; + private _imageApiService?: ImageApiService; + private _postApiService?: PostApiService; + + constructor(fetchFn: typeof fetch) { + this.fetchFn = fetchFn; + } + + get authApiService(): AuthApiService { + this._authApiService ??= new AuthApiServiceImpl(this.fetchFn); + return this._authApiService; + } + + get imageApiService(): ImageApiService { + this._imageApiService ??= new ImageApiServiceImpl(this.fetchFn); + return this._imageApiService; + } + + get postApiService(): PostApiService { + this._postApiService ??= new PostApiServiceImpl(this.fetchFn); + return this._postApiService; + } +} + +class Repositories { + private apiServices: ApiServices; + + private _authRepository?: AuthRepository; + private _imageRepository?: ImageRepository; + private _postRepository?: PostRepository; + + constructor(apiServices: ApiServices) { + this.apiServices = apiServices; + } + + get authRepository(): AuthRepository { + this._authRepository ??= new AuthRepositoryImpl(this.apiServices.authApiService); + return this._authRepository; + } + + get imageRepository(): ImageRepository { + this._imageRepository ??= new ImageRepositoryImpl(this.apiServices.imageApiService); + return this._imageRepository; + } + + get postRepository(): PostRepository { + this._postRepository ??= new PostRepositoryImpl(this.apiServices.postApiService); + return this._postRepository; + } +} + +class UseCases { + private repositories: Repositories; + + private _getCurrentUserUseCase?: GetCurrentUserUseCase; + private _uploadImageUseCase?: UploadImageUseCase; + private _getAllPostsUseCase?: GetAllPostsUseCase; + private _getPostUseCase?: GetPostUseCase; + + constructor(repositories: Repositories) { + this.repositories = repositories; + } + + get getCurrentUserUseCase(): GetCurrentUserUseCase { + this._getCurrentUserUseCase ??= new GetCurrentUserUseCase(this.repositories.authRepository); + return this._getCurrentUserUseCase; + } + + get uploadImageUseCase(): UploadImageUseCase { + this._uploadImageUseCase ??= new UploadImageUseCase(this.repositories.imageRepository); + return this._uploadImageUseCase; + } + + get getAllPostsUseCase(): GetAllPostsUseCase { + this._getAllPostsUseCase ??= new GetAllPostsUseCase(this.repositories.postRepository); + return this._getAllPostsUseCase; + } + get getPostUseCase(): GetPostUseCase { + this._getPostUseCase ??= new GetPostUseCase(this.repositories.postRepository); + return this._getPostUseCase; + } +} diff --git a/frontend/src/lib/home/framework/ui/Motto.svelte b/frontend/src/lib/home/framework/ui/Motto.svelte index feb1f01..efcf59f 100644 --- a/frontend/src/lib/home/framework/ui/Motto.svelte +++ b/frontend/src/lib/home/framework/ui/Motto.svelte @@ -1,5 +1,5 @@
import { getContext } from 'svelte'; - import UploadImageDialoag from './UploadImageDialoag.svelte'; + import UploadImageDialoag from '$lib/image/framework/ui/UploadImageDialoag.svelte'; import { ImageBloc, ImageEventType } from '$lib/image/adapter/presenter/imageBloc'; import { StatusType } from '$lib/common/adapter/presenter/asyncState'; import { toast } from 'svelte-sonner'; diff --git a/frontend/src/lib/image/framework/ui/UploadImageDialoag.svelte b/frontend/src/lib/image/framework/ui/UploadImageDialoag.svelte index a54860e..792e5cd 100644 --- a/frontend/src/lib/image/framework/ui/UploadImageDialoag.svelte +++ b/frontend/src/lib/image/framework/ui/UploadImageDialoag.svelte @@ -57,7 +57,7 @@ Upload Image -
+ @@ -77,7 +77,7 @@ - + diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 7a87f94..e8ee721 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -5,6 +5,11 @@ import '../app.css'; import '@fortawesome/fontawesome-free/css/all.min.css'; import { Toaster } from '$lib/common/framework/components/ui/sonner'; + import { Container } from '$lib/container'; + import { setContext } from 'svelte'; + + const container = new Container(fetch); + setContext(Container.name, container); diff --git a/frontend/src/routes/dashboard/+layout.svelte b/frontend/src/routes/dashboard/+layout.svelte index 47f29e8..0f6b7d7 100644 --- a/frontend/src/routes/dashboard/+layout.svelte +++ b/frontend/src/routes/dashboard/+layout.svelte @@ -1,24 +1,17 @@ diff --git a/frontend/src/routes/post/[id]/+page.server.ts b/frontend/src/routes/post/[id]/+page.server.ts index a065024..abab035 100644 --- a/frontend/src/routes/post/[id]/+page.server.ts +++ b/frontend/src/routes/post/[id]/+page.server.ts @@ -3,7 +3,8 @@ import { error } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ locals, params }) => { - const { postBloc } = locals; + const { container } = locals; + const postBloc = container.createPostBloc(); const state = await postBloc.dispatch({ event: PostEventType.PostLoadedEvent, diff --git a/frontend/src/routes/post/[id]/+page.svelte b/frontend/src/routes/post/[id]/+page.svelte index 5e14b25..34d156f 100644 --- a/frontend/src/routes/post/[id]/+page.svelte +++ b/frontend/src/routes/post/[id]/+page.svelte @@ -1,25 +1,17 @@