The Arsitektur Bersih menyarankan untuk membiarkan kasus penggunaan interactor memanggil implementasi aktual dari presenter (yang disuntikkan, berikut DIP) untuk menangani respon / display. Namun, saya melihat orang-orang mengimplementasikan arsitektur ini, mengembalikan data keluaran dari interaktor, dan kemudian membiarkan pengontrol (di lapisan adaptor) memutuskan bagaimana menanganinya. Apakah solusi kedua bocor tanggung jawab aplikasi keluar dari lapisan aplikasi, selain tidak secara jelas mendefinisikan port input dan output ke interaktor?
Port input dan output
Mempertimbangkan definisi Arsitektur Bersih , dan terutama diagram alir kecil yang menggambarkan hubungan antara pengontrol, use case berinteraksi, dan presenter, saya tidak yakin apakah saya benar memahami apa yang seharusnya "Use Case Output Port" seharusnya.
Arsitektur bersih, seperti arsitektur heksagonal, membedakan antara porta primer (metode) dan porta sekunder (interface yang akan diimplementasikan oleh adaptor). Mengikuti aliran komunikasi, saya berharap "Use Case Input Port" menjadi port utama (dengan demikian, hanya sebuah metode), dan "Use Case Output Port" sebuah antarmuka untuk diimplementasikan, mungkin argumen konstruktor yang mengambil adaptor yang sebenarnya, sehingga berinteraksi dapat menggunakannya.
Contoh kode
Untuk membuat contoh kode, ini bisa menjadi kode pengontrol:
Presenter presenter = new Presenter();
Repository repository = new Repository();
UseCase useCase = new UseCase(presenter, repository);
useCase->doSomething();
Antarmuka presenter:
// Use Case Output Port
interface Presenter
{
public void present(Data data);
}
Akhirnya, interaktor itu sendiri:
class UseCase
{
private Repository repository;
private Presenter presenter;
public UseCase(Repository repository, Presenter presenter)
{
this.repository = repository;
this.presenter = presenter;
}
// Use Case Input Port
public void doSomething()
{
Data data = this.repository.getData();
this.presenter.present(data);
}
}
Di interogator atau memanggil presenter
Interpretasi sebelumnya tampaknya dikonfirmasi oleh diagram yang disebutkan di atas, di mana hubungan antara pengontrol dan port input diwakili oleh panah padat dengan kepala "tajam" (UML untuk "asosiasi", yang berarti "memiliki", di mana controller "memiliki" use case), sedangkan hubungan antara presenter dan port output diwakili oleh panah padat dengan kepala "putih" (UML untuk "warisan", yang bukan yang untuk "implementasi", tetapi mungkin itu artinya).
Lebih jauh, dalam jawaban untuk pertanyaan lain ini , Robert Martin menggambarkan dengan tepat kasus penggunaan di mana interkom memanggil presenter atas permintaan baca:
Mengklik pada peta menyebabkan placePinController dipanggil. Ini mengumpulkan lokasi klik, dan data kontekstual lainnya, membangun struktur data placePinRequest dan meneruskannya ke PlacePinInteractor yang memeriksa lokasi pin, memvalidasi jika perlu, membuat entitas Place untuk merekam pin, membangun EditPlaceReponse objek dan meneruskannya ke EditPlacePresenter yang menampilkan layar editor tempat.
Untuk membuat ini bermain baik dengan MVC, saya bisa berpikir bahwa logika aplikasi yang secara tradisional akan masuk ke controller, di sini dipindahkan ke interaktor, karena kami tidak ingin ada logika aplikasi bocor di luar lapisan aplikasi. Pengontrol di lapisan adaptor hanya akan memanggil interaksor, dan mungkin melakukan beberapa konversi format data kecil dalam proses:
Perangkat lunak pada lapisan ini adalah seperangkat adaptor yang mengubah data dari format yang paling nyaman untuk kasus penggunaan dan entitas, ke format yang paling nyaman untuk beberapa agensi eksternal seperti Database atau Web.
dari artikel asli, berbicara tentang Adaptor Antarmuka.
Pada interaktor mengembalikan data
Namun, masalah saya dengan pendekatan ini adalah bahwa use case harus mengurus presentasi itu sendiri. Sekarang, saya melihat bahwa tujuan dari Presenter
antarmuka adalah untuk menjadi cukup abstrak untuk mewakili beberapa jenis presenter (GUI, Web, CLI, dll.), Dan itu benar-benar hanya berarti "output", yang merupakan sesuatu yang mungkin digunakan sangat baik, tapi tetap saja saya tidak sepenuhnya percaya diri dengan itu.
Sekarang, melihat-lihat aplikasi Web untuk arsitektur bersih, di sekitar saya hanya menemukan orang-orang menafsirkan port output sebagai metode mengembalikan beberapa DTO. Ini akan menjadi sesuatu seperti:
Repository repository = new Repository();
UseCase useCase = new UseCase(repository);
Data data = useCase.getData();
Presenter presenter = new Presenter();
presenter.present(data);
// I'm omitting the changes to the classes, which are fairly obvious
Ini menarik karena kami memindahkan tanggung jawab untuk "memanggil" presentasi keluar dari use case, jadi use case tidak peduli dengan mengetahui apa yang harus dilakukan dengan data lagi, bukan hanya dengan menyediakan data. Juga, dalam hal ini kita masih tidak melanggar aturan dependensi, karena use case masih tidak tahu apa-apa tentang lapisan luar.
Namun, use case tidak mengontrol saat ketika presentasi yang sebenarnya dilakukan lagi (yang mungkin berguna, misalnya untuk melakukan hal-hal tambahan pada saat itu, seperti mencatat, atau membatalkannya jika perlu). Juga, perhatikan bahwa kami kehilangan Port Input Use Case, karena sekarang controller hanya menggunakan getData()
metode (yang merupakan port output baru kami). Terlebih lagi, bagi saya tampaknya kita melanggar prinsip "katakan, jangan tanya" di sini, karena kami meminta beberapa data kepada interaktifer untuk melakukan sesuatu dengannya, daripada menyuruhnya melakukan hal yang sebenarnya dalam tempat pertama.
Ke titik
Jadi, adakah salah satu dari dua alternatif ini interpretasi "benar" dari Port Keluaran Penggunaan Kasus sesuai dengan Arsitektur Bersih? Apakah keduanya layak?