Ioc / DI - Mengapa saya harus mereferensikan semua lapisan / rakitan di titik masuk aplikasi?

123

(Terkait dengan pertanyaan ini, EF4: Mengapa pembuatan proxy harus diaktifkan saat pemuatan lambat diaktifkan? ).

Saya baru mengenal DI, jadi bersabarlah. Saya memahami bahwa penampung bertanggung jawab untuk membuat instance semua jenis terdaftar saya tetapi untuk melakukannya diperlukan referensi ke semua DLL dalam solusi saya dan referensi mereka.

Jika saya tidak menggunakan DI container, saya tidak perlu mereferensikan library EntityFramework di aplikasi MVC3 saya, hanya layer bisnis saya, yang akan mereferensikan layer DAL / Repo saya.

Saya tahu bahwa pada akhirnya semua DLL disertakan dalam folder bin tetapi masalah saya harus merujuknya secara eksplisit melalui "tambahkan referensi" di VS agar dapat mempublikasikan WAP dengan semua file yang diperlukan.

diegohb.dll
sumber
1
Kutipan dari buku Injeksi Ketergantungan di .NET, edisi kedua ini adalah versi yang lebih rumit dari jawaban Mark dan saya sendiri. Ini menjelaskan secara rinci konsep Akar Komposisi dan mengapa membiarkan jalur startup aplikasi bergantung pada setiap modul lain sebenarnya adalah hal yang baik.
Steven
Saya membaca tautan kutipan dan bab 1 itu, saya akan membeli buku itu karena saya sangat menikmati analogi dan penjelasan sederhana untuk masalah kompleks DI. Saya pikir Anda harus menyarankan jawaban baru, jawab dengan jelas "Anda tidak harus mereferensikan semua lapisan / rakitan di lapisan logis entri kecuali jika itu juga akar komposisi Anda," tautkan ke kutipan, dan posting gambar Gambar 3, dari kutipan.
diegohb

Jawaban:

194

Jika saya tidak menggunakan kontainer DI, saya tidak perlu mereferensikan pustaka EntityFramework di aplikasi MVC3 saya, hanya lapisan bisnis saya yang akan mereferensikan lapisan DAL / Repo saya.

Ya, itulah situasi yang saya hindari dengan susah payah :)

Dengan kode yang digabungkan erat, setiap pustaka mungkin hanya memiliki beberapa referensi, tetapi ini lagi-lagi memiliki referensi lain, membuat grafik dependensi yang dalam, seperti ini:

Grafik Dalam

Karena grafik ketergantungan dalam, itu berarti bahwa sebagian besar perpustakaan menyeret sepanjang banyak dependensi lain - misalnya dalam diagram, Perpustakaan C menyeret bersama Perpustakaan H, Perpustakaan E, Perpustakaan J, Perpustakaan M, Perpustakaan K dan Perpustakaan N . Hal ini mempersulit penggunaan kembali setiap library secara terpisah dari yang lain - misalnya dalam pengujian unit .

Namun, dalam aplikasi yang digabungkan secara longgar, dengan memindahkan semua referensi ke Akar Komposisi , grafik ketergantungan menjadi sangat rata :

Grafik Dangkal

Seperti yang diilustrasikan oleh warna hijau, sekarang dimungkinkan untuk menggunakan kembali Library C tanpa menyeret dependensi yang tidak diinginkan.

Namun, semua yang dikatakan, dengan banyak Kontainer DI, Anda tidak perlu menambahkan referensi keras ke semua pustaka yang diperlukan. Sebagai gantinya, Anda dapat menggunakan pengikatan terlambat baik dalam bentuk pemindaian rakitan berbasis konvensi (lebih disukai) atau konfigurasi XML.

Namun, ketika Anda melakukannya, Anda harus ingat untuk menyalin rakitan ke folder bin aplikasi, karena itu tidak lagi terjadi secara otomatis. Secara pribadi, saya jarang menganggap upaya ekstra itu sepadan.

Versi yang lebih terperinci dari jawaban ini dapat ditemukan dalam kutipan dari buku saya Injeksi Ketergantungan, Prinsip, Praktik, Pola .

Mark Seemann
sumber
3
Terima kasih banyak, sekarang ini sangat masuk akal .. saya perlu tahu apakah ini memang disengaja. Sejauh menegakkan penggunaan dependensi yang benar, saya telah mengimplementasikan proyek terpisah dengan bootstrapper DI saya seperti Steven yang disebutkan di bawah ini di mana saya mereferensikan pustaka lainnya. Proyek ini dirujuk oleh aplikasi titik masuk dan di akhir pembuatan penuh, ini menyebabkan semua dll yang diperlukan berada di folder bin. Terima kasih!
diegohb
2
@ Mark Seemann Apakah pertanyaan / jawaban ini khusus untuk Microsoft? Saya ingin tahu apakah ide untuk memindahkan semua dependensi ke "titik masuk aplikasi" masuk akal untuk proyek Java EE / Spring menggunakan Maven… terima kasih!
Grégoire C
5
Jawaban ini berlaku di luar .NET. Anda mungkin ingin merujuk ke bab Prinsip - prinsip Desain Paket Robert C. Martin di misalnya Pengembangan Perangkat Lunak Agile, Prinsip, Pola, dan Praktik
Mark Seemann
7
@AndyDangerGagne Composition Root adalah pola DI - kebalikan dari Service Locator . Dari perspektif Akar Komposisi, tidak ada tipe yang polimorfik; Akar Komposisi melihat semua tipe sebagai tipe konkret, dan karenanya, Prinsip Substitusi Liskov tidak berlaku untuk itu.
Mark Seemann
4
Sebagai aturan umum, antarmuka harus didefinisikan oleh klien yang menggunakannya ( APP, bab 11 ), jadi jika Library J membutuhkan antarmuka, itu harus didefinisikan di Library J. Itu adalah konsekuensi dari Prinsip Pembalikan Ketergantungan.
Mark Seemann
65

Jika saya tidak menggunakan kontainer DI, saya tidak perlu merujuk pustaka EntityFramework di aplikasi MVC3 saya

Bahkan saat menggunakan kontainer DI, Anda tidak harus membiarkan proyek MVC3 Anda mereferensikan EF, tetapi Anda (secara implisit) memilih untuk melakukan ini dengan mengimplementasikan Akar Komposisi (jalur startup tempat Anda membuat grafik objek) di dalam proyek MVC3 Anda. Jika Anda sangat ketat dalam melindungi batas arsitektur Anda menggunakan rakitan, Anda dapat memindahkan logika presentasi Anda ke proyek yang berbeda.

Saat Anda memindahkan semua logika terkait MVC (pengontrol, dll) dari proyek startup ke perpustakaan kelas, ini memungkinkan rakitan lapisan presentasi ini tetap terputus dari aplikasi lainnya. Proyek aplikasi web Anda sendiri akan menjadi shell yang sangat tipis dengan logika startup yang diperlukan. Proyek aplikasi web akan menjadi Akar Komposisi yang mereferensikan semua rakitan lainnya.

Mengekstrak logika presentasi ke pustaka kelas dapat mempersulit banyak hal saat bekerja dengan MVC. Akan lebih sulit untuk menghubungkan semuanya, karena pengontrol tidak ada dalam proyek startup (sementara tampilan, gambar, file css, kemungkinan besar harus tetap berada dalam proyek startup). Ini mungkin bisa dilakukan tetapi akan membutuhkan lebih banyak waktu untuk menyiapkan.

Karena kelemahannya, saya biasanya menyarankan untuk tetap menggunakan Akar Komposisi dalam proyek web. Banyak pengembang tidak ingin perakitan MVC mereka bergantung pada perakitan DAL, tetapi itu sebenarnya bukan masalah. Jangan lupa bahwa majelis adalah artefak penyebaran ; Anda membagi kode menjadi beberapa rakitan untuk memungkinkan kode diterapkan secara terpisah. Di sisi lain, lapisan arsitektur adalah artefak logis . Sangat mungkin (dan umum) untuk memiliki banyak lapisan dalam rakitan yang sama.

Dalam hal ini kita akan mendapatkan Composition Root (layer) dan Presentation Layer dalam proyek aplikasi web yang sama (sehingga dalam perakitan yang sama). Dan meskipun rakitan tersebut mereferensikan rakitan yang berisi DAL, Lapisan Presentasi masih tidak mereferensikan Lapisan Akses Data . Ini adalah perbedaan yang besar.

Tentu saja, ketika kita melakukan ini, kita kehilangan kemampuan kompilator untuk memeriksa aturan arsitektur ini pada waktu kompilasi, tetapi ini seharusnya tidak menjadi masalah. Kebanyakan aturan arsitektur sebenarnya tidak dapat diperiksa oleh kompiler dan selalu ada sesuatu yang masuk akal. Dan jika tidak ada akal sehat dalam tim Anda, Anda selalu dapat menggunakan tinjauan kode (yang harus selalu dilakukan oleh setiap tim oleh IMO). Anda juga dapat menggunakan alat seperti NDepend (yang bersifat komersial), yang membantu Anda memverifikasi aturan arsitektur Anda. Saat Anda mengintegrasikan NDepend dengan proses build Anda, ini dapat memperingatkan Anda ketika seseorang memeriksa kode yang melanggar aturan arsitektur tersebut.

Anda dapat membaca diskusi yang lebih terperinci tentang cara kerja Akar Komposisi di bab 4 buku saya Injeksi Ketergantungan, Prinsip, Praktik, Pola .

Steven
sumber
Proyek terpisah untuk bootstrap adalah solusi saya karena kami tidak memiliki ketergantungan dan saya belum pernah menggunakannya sebelumnya. Saya akan memeriksanya karena ini terdengar seperti cara yang lebih baik untuk mencapai apa yang saya coba lakukan ketika hanya akan ada 1 aplikasi akhir.
diegohb
1
Paragraf terakhir sangat bagus dan mulai membantu saya mengubah pikiran tentang seberapa ketat saya dalam menjaga lapisan dalam rakitan terpisah. Memiliki dua atau lebih lapisan logis dalam satu rakitan sebenarnya baik-baik saja jika Anda menggunakan proses lain seputar penulisan kode (seperti tinjauan kode) untuk memastikan tidak ada referensi kelas DAL dalam kode UI Anda dan sebaliknya.
BenM
6

Jika saya tidak menggunakan kontainer DI, saya tidak perlu merujuk pustaka EntityFramework di aplikasi MVC3 saya, hanya lapisan bisnis saya yang akan mereferensikan lapisan DAL / Repo saya.

Anda dapat membuat proyek terpisah yang disebut "DependencyResolver". Dalam proyek ini Anda harus mereferensikan semua perpustakaan Anda.

Sekarang Lapisan UI tidak memerlukan NHibernate / EF atau perpustakaan lain yang tidak relevan dengan UI kecuali Castle Windsor untuk direferensikan.

Jika Anda ingin menyembunyikan Castle Windsor dan DependencyResolver dari lapisan UI Anda, Anda dapat menulis HttpModule yang memanggil barang-barang registri IoC.

Saya hanya punya contoh untuk StructureMap:

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => EnsureDependenciesRegistered();
    }

    public void Dispose() { }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    ObjectFactory.ResetDefaults();

                    // Register all you dependencies here
                    ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry()));

                    new InitiailizeDefaultFactories().Configure();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

public class InitiailizeDefaultFactories
{
    public void Configure()
    {
        StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type);
          ...
    }
 }

DefaultControllerFactory tidak menggunakan container IoC secara langsung, tetapi mendelegasikan ke metode container IoC.

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public static Func<Type, object> GetController = type =>
    {
        throw new  InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!");
    };

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
        return GetController(controllerType) as Controller;
    }
}

The GetControllerdelegasi diatur dalam Registry StructureMap (di Windsor itu harus menjadi Installer).

Rookian
sumber
1
Saya suka ini bahkan lebih baik daripada apa yang akhirnya saya lakukan, modulnya bagus. jadi di mana saya akan melakukan panggilan ke Container.Dispose ()? ApplicationEnd atau EndRequest dalam modul ...?
diegohb
1
@Steven Karena Global.asax ada di MVC UI Layer Anda. HttpModule akan berada di Proyek DependencyResolver.
Rookian
1
Manfaat kecilnya adalah tidak ada yang dapat menggunakan container IoC di UI. Yaitu tidak ada yang dapat menggunakan Kontainer IoC sebagai pencari layanan di UI.
Rookian
1
Selain itu, ia melarang pengembang secara tidak sengaja menggunakan kode DAL di lapisan UI karena tidak ada referensi yang jelas ke rakitan di UI.
diegohb
1
Saya telah menemukan cara untuk melakukan hal yang sama menggunakan API registrasi generik Bootstrapper. Proyek UI saya mereferensikan Bootstrapper, proyek resolusi ketergantungan tempat saya memasang registrasi, dan proyek di Core saya (untuk antarmuka) tetapi tidak ada yang lain, bahkan Kerangka DI saya (SimpleInjector). Saya menggunakan OutputTo nuget untuk menyalin dll ke folder bin.
diegohb
0
  • Ada ketergantungan: jika sebuah objek membuat instance objek lain.
  • Tidak ada ketergantungan: jika suatu objek mengharapkan abstraksi (injeksi kontraktor, injeksi metode ...)
  • Majelis Referensi (referensi dll, webservices ..) tidak bergantung pada konsep ketergantungan, karena untuk menyelesaikan abstraksi dan dapat menyusun kode, lapisan harus mereferensikannya.
riadh gomri
sumber