Skip to content

refactor: MFA Step 3 — 전체 피처 수직 슬라이스 전환 및 공유 Domain/Data 모듈 삭제#74

Merged
gomminjae merged 1 commit intodevelopfrom
feature/mfa-step3-vertical-slice
Mar 27, 2026
Merged

refactor: MFA Step 3 — 전체 피처 수직 슬라이스 전환 및 공유 Domain/Data 모듈 삭제#74
gomminjae merged 1 commit intodevelopfrom
feature/mfa-step3-vertical-slice

Conversation

@gomminjae
Copy link
Copy Markdown
Owner

  • Explore, Subscribe, Bookmark, Detail, Search, MyPage 피처별 Domain/Data 타겟 생성
  • AppDIContainer에서 공유 Repository/UseCase 제거, MoyaProvider만 유지
  • CompositionRoot에서 피처별 인라인 DI 구성
  • Modules/Domain, Modules/Data 삭제

- Explore, Subscribe, Bookmark, Detail, Search, MyPage 피처별 Domain/Data 타겟 생성
- AppDIContainer에서 공유 Repository/UseCase 제거, MoyaProvider만 유지
- CompositionRoot에서 피처별 인라인 DI 구성
- Modules/Domain, Modules/Data 삭제

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a major architectural refactoring, migrating from centralized Domain and Data modules to a feature-based modular structure. New Domain and Data targets have been created for the Bookmark, Detail, Explore, MyPage, Search, and Subscribe features, with corresponding updates to the CompositionRoot for dependency management. The review feedback suggests improving the CompositionRoot by extracting redundant dependency creation logic into private factory methods and sharing use case instances within factory scopes to reduce unnecessary object instantiation and improve maintainability.

Comment on lines +45 to +53
static func registerGlobalDependencies() {
let exploreRepo = ExploreNewsletterRepositoryImpl(
provider: container.resolve(MoyaProvider<NewsletterAPI>.self)!
)
let exploreUseCase = ExploreNewsletterUseCaseImpl(repository: exploreRepo)
container.register(LoadOptionsUseCase.self) { _ in
LoadOptionsUseCaseImpl(newsletterUseCase: exploreUseCase)
}.inObjectScope(.container)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

ExploreNewsletterRepositoryImplExploreNewsletterUseCaseImpl 인스턴스를 생성하는 로직이 registerGlobalDependencies 메서드와 makeExploreFactory 메서드(88-92번째 줄) 양쪽에서 중복되고 있습니다. 코드 중복을 줄이고 유지보수성을 높이기 위해 이 로직을 별도의 private 팩토리 메서드로 추출하는 것을 고려해 보세요.

예를 들어, 다음과 같이 리팩토링할 수 있습니다:

@MainActor
enum CompositionRoot {
    // ...

    private static func makeExploreUseCase() -> ExploreNewsletterUseCase {
        let repo = ExploreNewsletterRepositoryImpl(
            provider: container.resolve(MoyaProvider<NewsletterAPI>.self)!
        )
        return ExploreNewsletterUseCaseImpl(repository: repo)
    }

    static func registerGlobalDependencies() {
        let exploreUseCase = makeExploreUseCase()
        container.register(LoadOptionsUseCase.self) { _ in
            LoadOptionsUseCaseImpl(newsletterUseCase: exploreUseCase)
        }.inObjectScope(.container)
    }

    static func makeExploreFactory() -> ExploreViewFactory {
        return ExploreViewFactoryImpl(viewModelProvider: {
            let useCase = makeExploreUseCase()
            return ExploreViewModel(useCase: useCase)
        })
    }

    // ...
}

이렇게 하면 의존성 생성 로직이 한 곳에서 관리되어 향후 변경이 필요할 때 더 용이합니다.

Comment on lines 144 to 170
static func makeMypageFactory() -> MypageViewFactory {
let userRepo = MypageUserRepositoryImpl(
provider: container.resolve(MoyaProvider<UserAPI>.self)!
)
let statsRepo = MypageStatsRepositoryImpl(
articleProvider: container.resolve(MoyaProvider<ArticleAPI>.self)!,
newsletterProvider: container.resolve(MoyaProvider<NewsletterAPI>.self)!
)
return MypageViewFactoryImpl(
mypageViewModelProvider: {
let userUseCase = container.resolve(UserUseCase.self)!
let userUseCase = MypageUserUseCaseImpl(repository: userRepo)
let profileUseCase = ProfileUseCaseImpl(userUseCase: userUseCase)
return MypageViewModel(useCase: userUseCase, profileUseCase: profileUseCase)
},
recoveryViewModelProvider: {
let useCase = container.resolve(UserUseCase.self)!
return RecoveryViewModel(useCase: useCase)
let userUseCase = MypageUserUseCaseImpl(repository: userRepo)
return RecoveryViewModel(useCase: userUseCase)
},
withdrawViewModelProvider: {
let userUseCase = container.resolve(UserUseCase.self)!
let newsletterUseCase = container.resolve(NewsletterUseCase.self)!
let articleUseCase = container.resolve(ArticleUseCase.self)!
let userUseCase = MypageUserUseCaseImpl(repository: userRepo)
let statsUseCase = MypageStatsUseCaseImpl(repository: statsRepo)
return WithdrawViewModel(
userUseCase: userUseCase,
newsletterUseCase: newsletterUseCase,
articleUseCase: articleUseCase
statsUseCase: statsUseCase
)
}
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

makeMypageFactory 내에서 MypageUserUseCaseImpl 인스턴스가 여러 ViewModel Provider 클로저마다 생성되고 있습니다. MypageUserUseCaseImpl가 상태를 가지지 않는다면, 팩토리 메서드 스코프 내에서 한 번만 생성하여 여러 ViewModel에서 공유하도록 리팩토링하는 것이 더 효율적일 것 같습니다.

이렇게 하면 불필요한 인스턴스 생성을 줄이고 코드를 더 간결하게 만들 수 있습니다.

    static func makeMypageFactory() -> MypageViewFactory {
        let userRepo = MypageUserRepositoryImpl(
            provider: container.resolve(MoyaProvider<UserAPI>.self)!
        )
        let statsRepo = MypageStatsRepositoryImpl(
            articleProvider: container.resolve(MoyaProvider<ArticleAPI>.self)!,
            newsletterProvider: container.resolve(MoyaProvider<NewsletterAPI>.self)!
        )
        let userUseCase = MypageUserUseCaseImpl(repository: userRepo)
        let statsUseCase = MypageStatsUseCaseImpl(repository: statsRepo)
        return MypageViewFactoryImpl(
            mypageViewModelProvider: {
                let profileUseCase = ProfileUseCaseImpl(userUseCase: userUseCase)
                return MypageViewModel(useCase: userUseCase, profileUseCase: profileUseCase)
            },
            recoveryViewModelProvider: {
                return RecoveryViewModel(useCase: userUseCase)
            },
            withdrawViewModelProvider: {
                return WithdrawViewModel(
                    userUseCase: userUseCase,
                    statsUseCase: statsUseCase
                )
            }
        )
    }

@gomminjae gomminjae merged commit 3dcb7fc into develop Mar 27, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant