DIP (Dependency Inversion Principle) — ang ikalimang prinsipyo ng SOLID na tumutukoy sa mga patakaran ng pagbuo ng mga dependency sa pagitan ng mga module: ang mga module ng mas mataas na antas ay hindi dapat umasa sa mga module ng mas mababang antas, pareho ay dapat umasa sa mga abstraction. Ang mga abstraction ay hindi dapat umasa sa mga detalye — ang mga detalye ay dapat umasa sa mga abstraction. Ang prinsipyong ito, na inilarawan ni Robert Martin sa Clean Architecture (2017), ay nasa pundasyon ng maluwag na magkakaugnay na arkitektura. Ayon sa aklat na ito, ang prinsipyo ng inversion ng dependencies ay nag-aalis ng matigas na mga ugnayan sa pagitan ng mga layer ng application.
Mga Pangunahing Punto
DIP (Dependency Inversion Principle) — prinsipyo ng inversion ng dependencies na binabaligtad ang tradisyonal na pananaw tungkol sa direksyon ng mga dependency sa pagitan ng mga module. Ang mga module ng mataas na antas (business logic) ay hindi dapat direktang umasa sa mga module ng mababang antas (database, network, UI). Sa halip, ang parehong antas ay umaasa sa mga abstraction na tinukoy sa module ng mas mataas na antas.
Ang pormal na pormulasyon ng DIP ay may dalawang patakaran: A — ang mga module ng mas mataas na antas ay hindi dapat umasa sa mga module ng mas mababang antas, pareho ay dapat umasa sa mga abstraction. B — ang mga abstraction ay hindi dapat umasa sa mga detalye, ang mga detalye ay dapat umasa sa mga abstraction. Ang pangalawang patakaran ay bunga ng una: kung ang abstraction ay umaasa sa mga detalye, hindi ito maaaring maging matatag na pundasyon para sa module ng mas mataas na antas.
Kung walang DIP, ang tipikal na arkitektura ay ganito: BusinessLogic → DatabaseRepository — ang business logic ay direktang umaasa sa isang konkretong repository. Sa DIP: BusinessLogic → DatabaseServiceInterface ← DatabaseRepository. Ang BusinessLogic ay hindi alam ang pagkakaroon ng DatabaseRepository, alam lamang nito ang interface ng DatabaseService na ipinapatupad sa labas ng business logic.
Ang inversion ay nangangahulugan na ang daloy ng kontrol at daloy ng mga dependency ay nakadirekta sa magkasalungat na direksyon. Ang daloy ng kontrol ay mula sa itaas pababa: UI → ViewModel → UseCase → Repository. Ang daloy ng mga dependency ay mula sa ibaba pataas: Repository ay nag-iimplementa ng interface na tinukoy sa UseCase. Repository (mababang antas) ay umaasa sa UseCase (mataas na antas).
Ang inversion na ito ay ang pangunahing pagkakaiba sa pagitan ng DIP at ordinaryong paghahati sa mga layer. Sa tradisyonal na layered na arkitektura, bawat layer ay umaasa sa layer sa ibaba. Sa arkitektura na may DIP, lahat ng layer ay umaasa sa mga abstraction, at ang implementasyon ng mga abstraction na ito ay nasa layer ng imprastraktura na kumokonekta
sa mga itaas na layer sa pamamagitan ng mga mekanismo ng DI.
Ang mekanismo ng DIP ay naisasakatuparan sa pamamagitan ng pagtukoy ng mga abstraction sa mga module ng mas mataas na antas at pagpapatupad ng mga ito sa mga module ng mas mababang antas. Ang module ng mas mataas na antas ay nagdedeklara ng interface para sa kinakailangang functionality. Ang module ng mas mababang antas ay nag-iimplementa ng interface na ito. Ang pag-assemble (wiring) ay nagaganap sa antas ng composition root ng application.
Ang proseso ng paglalapat ng DIP sa umiiral na code: i-extract ang interface para sa module ng mababang antas, ilipat ang interface na ito sa module ng mas mataas na antas (o sa isang hiwalay na layer ng abstraction), isulat muli ang dependency ng itaas na module sa interface, pilitin ang module ng mababang antas na i-implement ang interface na ito. Pagkatapos ng mga hakbang na ito, ang direksyon ng dependency ay nagbago sa kabaligtaran.
Ang DIP ay nangangailangan ng mekanismo ng composition root — punto sa application kung saan ang lahat ng dependency ay nilikha at pinag-uugnay. Sa Android ito ay Application.get() o Hilt component, sa iOS — AppDelegate o SceneDelegate. Ang composition root ay ang tanging lugar kung saan alam ng code ang mga konkretong implementasyon.
Ang DIP ay bumubuo ng mga hangganang arkitektural sa pagitan ng mga layer ng application. Kapag ang ViewModel ay umaasa sa interface ng UserRepository, isang hangganan ang lumilitaw sa pagitan ng presentation at domain layer: ViewModel (presentation) ay hindi alam kung saan nagmumula ang data. Ang hangganan na ito ay nagpapahintulot na baguhin ang implementasyon ng UserRepository (Room → REST → Mock) nang hindi naaapektuhan ang ViewModel. Kung mas marami ang mga hangganang ito, mas matatag ang application sa mga pagbabago ng framework at library.
Sa arkitektura ng Android na inirerekomenda ng Google, ang DIP ay ipinapatupad sa pamamagitan ng UseCase na nasa domain layer at umaasa sa mga interface ng Repository. Ang RepositoryImpl ay nasa data layer at nag-iimplementa ng mga interface na ito. Ang presentation layer (ViewModel) ay umaasa sa UseCase. Ang direksyon ng mga dependency ay mula presentation patungong domain, mula domain patungong data — ngunit walang layer na alam ang mga konkretong implementasyon ng ibang layer.
Ang DIP at DI ay madalas na napagkakamalan, ngunit iba't ibang konsepto ang mga ito. DIP — prinsipyong arkitektural (ANO ang dapat gawin: umasa sa mga abstraction). DI — pattern ng implementasyon (PAANO ito gagawin: magpasa ng mga dependency sa pamamagitan ng constructor). Sinasagot ng DIP ang tanong na sa ano dapat ibatay ang mga module?
, DI — paano nakukuha ng mga object ang kanilang mga dependency?
.
Dependency Injection — paraan ng pag-inject ng mga dependency sa isang object sa pamamagitan ng constructor, method, o property. Kapag sa isang Kotlin class ang interface ng Repository ay ipinapasa sa pamamagitan ng constructor — ito ay DI. At ang katotohanan na ang ViewModel class ay umaasa sa interface ng Repository, hindi sa konkretong implementasyon ng RoomRepository — ito ay DIP. DI — kasangkapan, DIP — layunin.
Maaaring sundin ang DIP nang walang DI framework: ang manu-manong pag-uugnay ng mga dependency sa composition root ay DI rin (manual DI). Maaaring gumamit ng DI framework (Dagger, Hilt, Koin) habang lumalabag sa DIP: kung ang ViewModel ay direktang gumagawa ng Repository object sa pamamagitan ng new() — ang DIP ay nilabag, kahit na naka-install ang framework. DIP — desisyong arkitektural, DI — teknikal na detalye.
Tingnan natin ang halimbawa sa Android ng paglalapat ng DIP sa layer ng data. Kung walang DIP, ang ViewModel ay direktang gumagawa ng RoomDatabase at DAO. Sa DIP — ang ViewModel ay umaasa sa interface ng UserRepository, at ang konkretong implementasyon ng RoomUserRepository ay ibinibigay mula sa labas.
// Ang abstraction ay pag-aari ng domain layer (mas mataas na antas)
interface UserRepository {
fun getUser(id: Int): User
}
// Ang domain layer ay umaasa lamang sa abstraction
class GetUserUseCase(
private val repo: UserRepository
) {
fun execute(id: Int): User = repo.getUser(id)
}
// Ang implementasyon sa data layer ay umaasa sa abstraction ng domain layer
class RoomUserRepository(
private val dao: UserDao
) : UserRepository {
override fun getUser(id: Int): User {
return dao.getById(id)
}
}
// Composition root
class AppModule {
fun provideUserRepository(dao: UserDao): UserRepository {
return RoomUserRepository(dao)
}
}
Halimbawa sa iOS na may Application Coordinator at protocol para sa navigation:
// Abstraction ng navigation sa domain layer
protocol AuthNavigation {
func navigateToHome()
func navigateToLogin()
}
// Ang ViewModel ay umaasa sa abstraction, hindi sa UIKit
final class AuthViewModel {
private let navigation: AuthNavigation
init(navigation: AuthNavigation) {
self.navigation = navigation
}
func onLoginSuccess() {
navigation.navigateToHome()
}
}
// Coordinator (UIKit layer) ay nag-iimplementa ng protocol ng domain layer
final class AppCoordinator: AuthNavigation {
func navigateToHome() {
// UIKit navigation code
}
func navigateToLogin() {
// UIKit navigation code
}
}
Mahalagang punto: Ang AuthViewModel (domain) ay hindi alam ang pagkakaroon ng AppCoordinator (UIKit). Alam lamang nito ang protocol na AuthNavigation. Kung bukas ay papalitan ang UIKit ng SwiftUI — ang AuthViewModel ay hindi nangangailangan ng pagbabago. Ginagawa ng DIP ang domain layer na independyente sa mga framework at UI library.
Hilt — ang karaniwang tool ng DI para sa Android, inirerekomenda ng Google. Naka-integrate sa Jetpack, sumusuporta sa ViewModel, Fragment, Service, at iba pang bahagi ng Android. Ina-automate ng Hilt ang paglikha ng composition root sa pamamagitan ng mga anotasyon na @Module, @Provides, @Inject. Ang paggamit ng Hilt ay hindi ginagarantiya ang pagsunod sa DIP — ang interface ng UserRepository ay dapat tukuyin sa domain layer, hindi sa data layer.
Koin — magaan na DI framework para sa Kotlin nang walang code generation at annotation processor. Ang DSL ng Koin (module, single, factory) ay mas madaling matutunan, ngunit ang pagsusuri ng mga dependency ay nangyayari sa runtime, hindi sa compile time. Ang Koin ay sikat sa mga multiplatform na proyekto (KMP) dahil sa suporta nito sa iOS.
Dagger 2 — hinalinhan ng Hilt, ginagamit pa rin sa malalaking proyekto. Ang Dagger ay gumagawa ng DI code sa compile time, na nagbibigay ng maximum na performance at error diagnosis sa build stage. Ang Hilt ay binuo sa ibabaw ng Dagger at nagbibigay ng pinasimpleng API. Para sa mga bagong proyekto, inirerekomenda ng Google ang Hilt bilang pangunahing DI framework.
Ang mga DI module ay dapat tumugma sa mga arkitektural na layer at hatiin sa DomainModule, DataModule, PresentationModule. Ang DomainModule ay nagbibigay lamang ng mga abstraction at UseCase. Ang DataModule ay nagbibigay ng mga implementasyon para sa mga abstraction. Ang PresentationModule ay nag-uugnay ng ViewModel sa UseCase. Ang ganitong organisasyon ay ginagarantiya na ang domain layer ay nananatiling independyente mula sa mga library ng imprastraktura.
Sa paglipat sa pagitan ng mga DI framework (halimbawa, mula Koin patungong Hilt), ang istraktura ng DomainModule ay hindi nagbabago — tanging ang mga paraan ng pag-uugnay sa DataModule at PresentationModule ang nagbabago. Tinitiyak ng DIP ang paghihiwalay ng domain logic, at ang DI framework ay ang teknikal na mekanismo ng pag-uugnay.
Mga Madalas na Itanong
DIP ay kinakailangan sa mga hangganang arkitektural — sa pagitan ng mga layer ng application (domain → data, presentation → domain). Sa loob ng isang layer, ang DIP ay maaaring labis. Halimbawa, ang utility class na StringFormatter sa loob ng domain layer ay hindi nangangailangan ng interface — kung walang mga dahilan para palitan ito.
Hindi. DIP — prinsipyo: ang mga module ay dapat umasa sa mga abstraction. DI — pattern: ang object ay tumatanggap ng mga dependency mula sa labas, hindi ito lumilikha ng mga ito mismo. Ang DI ay paraan ng pagpapatupad ng DIP, ngunit ang DIP ay maaaring sundin nang walang DI (sa pamamagitan ng mga factory o service locator). Ang DI nang walang DIP ay posible ngunit walang halagang arkitektural.
Ang mga interface ay pag-aari ng module na gumagamit ng mga ito, hindi ng module na nag-iimplementa ng mga ito. Ang UserRepository ay idinedeklara sa domain layer at iniimplementa sa data layer. Ito ang pangunahing patakaran ng DIP: ang may-ari ng abstraction ay ang consumer, hindi ang provider ng implementasyon.
Ginagawa ng DIP na posible ang pag-test sa mga isolated na layer. Ang ViewModel na umaasa sa UserRepository (interface) ay tinetest na may mock implementasyon nang walang database. Kung walang DIP, ang ViewModel ay aasa sa RoomUserRepository at mangangailangan ng database configuration para sa bawat test. Ang DIP + DI ay nagbibigay ng kumpletong paghihiwalay ng mga module sa pag-test.
Hilt — karaniwang pagpili para sa mga proyekto ng Android, inirerekomenda ng Google. Koin — alternatibo para sa mga proyekto ng Kotlin Multiplatform. Dagger 2 — para sa mga umiiral na proyekto kung saan ang paglipat sa Hilt ay hindi makatwiran. Ang pagpili ng framework ay hindi nag-aalis ng pangangailangan na sundin ang DIP sa antas ng arkitektura.
Buod
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din