Seberapa akurat “Logika bisnis harus dalam layanan, bukan dalam model”?

398

Situasi

Sebelumnya malam ini saya memberikan jawaban atas pertanyaan di StackOverflow.

Pertanyaan:

Mengedit objek yang ada harus dilakukan di lapisan repositori atau dalam layanan?

Misalnya jika saya memiliki Pengguna yang memiliki hutang. Saya ingin mengubah utangnya. Haruskah saya melakukannya di UserRepository atau dalam layanan misalnya BuyingService dengan mendapatkan objek, mengedit dan menyimpannya?

Jawabanku:

Anda harus meninggalkan tanggung jawab untuk memutasikan suatu objek ke objek yang sama dan menggunakan repositori untuk mengambil objek ini.

Contoh situasi:

class User {
    private int debt; // debt in cents
    private string name;

    // getters

    public void makePayment(int cents){
        debt -= cents;
    }
}

class UserRepository {
    public User GetUserByName(string name){
        // Get appropriate user from database
    }
}

Komentar yang saya terima:

Logika bisnis harus benar-benar dalam layanan. Tidak dalam model.

Apa kata internet?

Jadi, ini membuat saya mencari karena saya tidak pernah (secara sadar) menggunakan lapisan layanan. Saya mulai membaca tentang pola Lapisan Layanan dan pola Unit Kerja tetapi sejauh ini saya tidak bisa mengatakan saya yakin lapisan layanan harus digunakan.

Ambil contoh artikel ini oleh Martin Fowler tentang anti-pola Model Domain Anemik:

Ada objek, banyak yang dinamai dengan kata benda di ruang domain, dan objek ini terhubung dengan hubungan dan struktur kaya yang dimiliki oleh model domain yang sebenarnya. Tangkapan datang ketika Anda melihat perilaku, dan Anda menyadari bahwa hampir tidak ada perilaku pada benda-benda ini, membuat mereka sedikit lebih dari sekantong getter dan setter. Memang sering model-model ini datang dengan aturan desain yang mengatakan bahwa Anda tidak boleh menempatkan logika domain di objek domain. Sebaliknya ada satu set objek layanan yang menangkap semua logika domain. Layanan ini hidup di atas model domain dan menggunakan model domain untuk data.

(...) Logika yang harus ada dalam objek domain adalah logika domain - validasi, perhitungan, aturan bisnis - apa pun yang Anda suka menyebutnya.

Bagi saya, ini persis seperti apa situasinya: Saya menganjurkan manipulasi data objek dengan memperkenalkan metode di dalam kelas yang melakukan hal itu. Namun saya menyadari bahwa ini harus diberikan dengan cara apa pun, dan mungkin lebih berkaitan dengan bagaimana metode ini dipanggil (menggunakan repositori).

Saya juga merasa bahwa dalam artikel itu (lihat di bawah), Layer Layanan lebih dianggap sebagai façade yang mendelegasikan pekerjaan ke model yang mendasarinya, daripada lapisan padat karya yang sebenarnya.

Application Layer [namanya untuk Service Layer]: Menentukan pekerjaan yang seharusnya dilakukan oleh perangkat lunak dan mengarahkan objek domain ekspresif untuk menyelesaikan masalah. Tugas-tugas yang bertanggung jawab atas lapisan ini bermakna bagi bisnis atau diperlukan untuk interaksi dengan lapisan aplikasi sistem lain. Lapisan ini dijaga tetap tipis. Itu tidak mengandung aturan atau pengetahuan bisnis, tetapi hanya mengoordinasikan tugas dan delegasi yang bekerja untuk kolaborasi objek domain di lapisan berikutnya ke bawah. Itu tidak memiliki keadaan yang mencerminkan situasi bisnis, tetapi bisa memiliki keadaan yang mencerminkan kemajuan tugas bagi pengguna atau program.

Yang diperkuat di sini :

Antarmuka layanan. Layanan memperlihatkan antarmuka layanan tempat semua pesan masuk dikirim. Anda dapat menganggap antarmuka layanan sebagai fasad yang memaparkan logika bisnis yang diterapkan dalam aplikasi (biasanya, logika di lapisan bisnis) kepada konsumen potensial.

Dan di sini :

Lapisan layanan harus tanpa aplikasi atau logika bisnis apa pun dan harus berfokus terutama pada beberapa masalah. Seharusnya membungkus panggilan Layer Bisnis, menerjemahkan Domain Anda dalam bahasa umum yang dapat dipahami klien Anda, dan menangani media komunikasi antara server dan klien yang meminta.

Ini sangat berbeda dengan sumber daya lain yang berbicara tentang Lapisan Layanan:

Lapisan layanan harus terdiri dari kelas dengan metode yang merupakan unit kerja dengan tindakan yang termasuk dalam transaksi yang sama.

Atau jawaban kedua untuk pertanyaan yang sudah saya tautkan:

Pada titik tertentu, aplikasi Anda akan menginginkan beberapa logika bisnis. Selain itu, Anda mungkin ingin memvalidasi input untuk memastikan bahwa tidak ada sesuatu yang jahat atau tidak diminta yang diminta. Logika ini termasuk dalam lapisan layanan Anda.

"Larutan"?

Mengikuti pedoman dalam jawaban ini , saya datang dengan pendekatan berikut yang menggunakan Lapisan Layanan:

class UserController : Controller {
    private UserService _userService;

    public UserController(UserService userService){
        _userService = userService;
    } 

    public ActionResult MakeHimPay(string username, int amount) {
        _userService.MakeHimPay(username, amount);
        return RedirectToAction("ShowUserOverview");
    }

    public ActionResult ShowUserOverview() {
        return View();
    }
}

class UserService {
    private IUserRepository _userRepository;

    public UserService(IUserRepository userRepository) {
        _userRepository = userRepository;
    }

    public void MakeHimPay(username, amount) {
        _userRepository.GetUserByName(username).makePayment(amount);
    }
}

class UserRepository {
    public User GetUserByName(string name){
        // Get appropriate user from database
    }
}

class User {
    private int debt; // debt in cents
    private string name;

    // getters

    public void makePayment(int cents){
        debt -= cents;
    }
}

Kesimpulan

Semua bersama-sama tidak banyak yang berubah di sini: kode dari controller telah pindah ke lapisan layanan (yang merupakan hal yang baik, jadi ada sisi positif dari pendekatan ini). Namun ini sepertinya tidak ada hubungannya dengan jawaban asli saya.

Saya menyadari bahwa pola desain adalah pedoman, bukan aturan yang ditetapkan untuk diterapkan bila memungkinkan. Namun saya belum menemukan penjelasan yang pasti tentang lapisan layanan dan bagaimana seharusnya dianggap.

  • Apakah ini sarana untuk mengekstrak logika dari controller dan memasukkannya ke dalam layanan?

  • Apakah ini seharusnya membentuk kontrak antara pengontrol dan domain?

  • Haruskah ada lapisan antara domain dan lapisan layanan?

Dan, yang tak kalah pentingnya: mengikuti komentar asli

Logika bisnis harus benar-benar dalam layanan. Tidak dalam model.

  • Apakah ini benar?

    • Bagaimana saya memperkenalkan logika bisnis saya dalam layanan, bukan model?
Jeroen Vannevel
sumber
6
Saya memperlakukan lapisan layanan sebagai tempat untuk meletakkan bagian skrip transaksi yang tidak dapat dihindari yang bertindak berdasarkan akar agregat. Jika lapisan layanan saya menjadi terlalu rumit, itu menandakan saya bahwa saya bergerak ke arah Model Anemik dan model domain saya perlu perhatian dan ulasan. Saya juga mencoba untuk menempatkan logika di SL yang tidak akan diduplikasi oleh sifatnya.
Pavel Voronin
Saya pikir layanan adalah bagian dari lapisan Model. Apakah saya salah dalam memikirkan itu?
Florian Margaine
Saya menggunakan aturan praktis: jangan bergantung pada apa yang tidak Anda butuhkan; kalau tidak, saya mungkin (biasanya negatif) dipengaruhi oleh perubahan pada bagian yang tidak saya butuhkan. Sebagai akibatnya, saya berakhir dengan banyak peran yang jelas. Objek data saya berisi perilaku selama semua klien menggunakannya. Kalau tidak, saya memindahkan perilaku ke kelas menerapkan peran yang diperlukan.
beluchin
1
Logika kontrol aliran aplikasi termasuk dalam pengontrol. Logika akses data termasuk dalam repositori. Logika validasi termasuk dalam lapisan layanan. Lapisan layanan adalah lapisan tambahan dalam aplikasi ASP.NET MVC yang memediasi komunikasi antara lapisan pengontrol dan repositori. Lapisan layanan berisi logika validasi bisnis. gudang. asp.net/mvc/overview/older-versions-1/models-data/…
Kbdavis07
3
IMHO, model domain OOP-style yang benar memang harus menghindari "layanan bisnis" dan menjaga logika bisnis dalam model. Tetapi praktik menunjukkan bahwa sangat menggoda untuk membuat lapisan bisnis besar dengan metode yang diberi nama berbeda dan juga untuk melakukan transaksi (atau unit kerja) di seluruh metode. Jauh lebih mudah untuk memproses beberapa kelas model dalam satu metode layanan bisnis daripada merencanakan hubungan antara kelas-kelas model tersebut, karena dengan begitu Anda akan memiliki beberapa pertanyaan sulit untuk dijawab: di mana agregat root? Di mana saya harus memulai / melakukan transaksi basis data? Dll
JustAMartin

Jawaban:

369

Untuk menentukan apa tanggung jawab suatu layanan , Anda harus terlebih dahulu menentukan apa layanan itu.

Layanan bukan istilah perangkat lunak kanonik atau generik. Bahkan, akhiran Servicepada nama kelas adalah banyak seperti banyak difitnah Manajer : Ini memberitahu Anda hampir tidak ada tentang apa benda itu sebenarnya tidak .

Pada kenyataannya, apa yang harus dilakukan oleh suatu layanan sangat spesifik untuk arsitektur:

  1. Dalam arsitektur berlapis tradisional, layanan secara harfiah identik dengan lapisan logika bisnis . Ini lapisan antara UI dan Data. Karena itu, semua aturan bisnis masuk ke layanan. Lapisan data seharusnya hanya memahami operasi CRUD dasar, dan lapisan UI hanya harus berurusan dengan pemetaan presentasi DTO ke dan dari objek bisnis.

  2. Dalam arsitektur terdistribusi gaya RPC (SOAP, UDDI, BPEL, dll.), Layanan ini adalah versi logis dari titik akhir fisik . Ini pada dasarnya adalah kumpulan operasi yang ingin disediakan oleh pengelola sebagai API publik. Berbagai panduan praktik terbaik menjelaskan bahwa operasi layanan seharusnya menjadi operasi tingkat bisnis dan bukan CRUD, dan saya cenderung setuju.

    Namun, karena merutekan segala sesuatu melalui layanan jarak jauh yang sebenarnya dapat sangat merusak kinerja, biasanya yang terbaik adalah tidak membiarkan layanan ini benar-benar mengimplementasikan logika bisnis itu sendiri; alih-alih, mereka harus membungkus satu set objek bisnis "internal". Layanan tunggal mungkin melibatkan satu atau beberapa objek bisnis.

  3. Dalam arsitektur MVP / MVC / MVVM / MV *, layanan tidak ada sama sekali. Atau jika mereka melakukannya, istilah ini digunakan untuk merujuk ke objek generik yang dapat disuntikkan ke model controller atau view. Logika bisnis ada dalam model Anda . Jika Anda ingin membuat "objek layanan" untuk mengatur operasi yang rumit, itu terlihat sebagai detail implementasi. Banyak orang, sayangnya, menerapkan MVC seperti ini, tetapi itu dianggap sebagai anti-pola ( Anemic Domain Model ) karena model itu sendiri tidak melakukan apa-apa, itu hanya sekumpulan properti untuk UI.

    Beberapa orang keliru berpikir bahwa mengambil metode 100-line controller dan mendorong semuanya ke dalam layanan entah bagaimana membuat arsitektur yang lebih baik. Sebenarnya tidak; yang dilakukannya hanyalah menambahkan lapisan tipuan yang mungkin tidak perlu. Secara praktis , controller masih melakukan pekerjaan, itu hanya melakukannya melalui objek "pembantu" yang tidak disebutkan namanya. Saya sangat merekomendasikan presentasi Wicked Domain Model Jimmy Bogard untuk contoh yang jelas tentang cara mengubah model domain anemia menjadi yang bermanfaat. Ini melibatkan pemeriksaan yang teliti terhadap model yang Anda ungkap dan operasi mana yang benar-benar valid dalam konteks bisnis .

    Misalnya, jika database Anda berisi Pesanan, dan Anda memiliki kolom untuk Jumlah Total, aplikasi Anda mungkin tidak boleh benar-benar mengubah bidang itu ke nilai arbitrer, karena (a) riwayatnya dan (b) seharusnya ditentukan oleh apa yang ada dalam urutan serta mungkin beberapa data / aturan sensitif waktu lainnya. Membuat layanan untuk mengelola Pesanan tidak serta merta menyelesaikan masalah ini, karena kode pengguna masih dapat mengambil objek Pesanan yang sebenarnya dan mengubah jumlahnya. Sebaliknya, pesanan itu sendiri harus bertanggung jawab untuk memastikan bahwa pesanan itu hanya dapat diubah dengan cara yang aman dan konsisten.

  4. Dalam DDD, layanan dimaksudkan secara khusus untuk situasi ketika Anda memiliki operasi yang tidak semestinya menjadi milik agregat root . Anda harus berhati-hati di sini, karena seringkali kebutuhan akan layanan dapat menyiratkan bahwa Anda tidak menggunakan akar yang benar. Tetapi dengan asumsi Anda melakukannya, suatu layanan digunakan untuk mengoordinasikan operasi lintas banyak akar, atau terkadang untuk menangani masalah yang sama sekali tidak melibatkan model domain (seperti, mungkin, menulis informasi ke basis data BI / OLAP).

    Salah satu aspek penting dari layanan DDD adalah bahwa ia diizinkan untuk menggunakan skrip transaksi . Ketika bekerja pada aplikasi besar, Anda sangat mungkin pada akhirnya akan mengalami contoh di mana itu hanya cara yang lebih mudah untuk mencapai sesuatu dengan prosedur T-SQL atau PL / SQL daripada harus repot dengan model domain. Ini tidak masalah, dan itu termasuk dalam Layanan.

    Ini adalah perubahan radikal dari definisi layanan arsitektur berlapis. Lapisan layanan merangkum objek domain; layanan DDD merangkum apa pun yang tidak ada dalam objek domain dan tidak masuk akal.

  5. Dalam Arsitektur Berorientasi Layanan, layanan dianggap sebagai otoritas teknis untuk kemampuan bisnis. Itu berarti bahwa itu adalah pemilik eksklusif dari bagian tertentu dari data bisnis dan tidak ada yang diizinkan untuk menyentuh data itu - bahkan tidak hanya untuk membacanya .

    Dengan kebutuhan, layanan sebenarnya merupakan proposisi ujung ke ujung dalam SOA. Artinya, layanan bukanlah komponen spesifik seperti seluruh tumpukan , dan seluruh aplikasi Anda (atau seluruh bisnis Anda) adalah seperangkat layanan ini yang berjalan berdampingan tanpa persimpangan kecuali pada lapisan pesan dan UI. Setiap layanan memiliki data sendiri, aturan bisnisnya sendiri, dan UI sendiri. Mereka tidak perlu mengatur satu sama lain karena mereka diharapkan memiliki bisnis yang selaras - dan, seperti bisnis itu sendiri, setiap layanan memiliki serangkaian tanggung jawab masing-masing dan beroperasi kurang lebih secara independen dari yang lain.

    Jadi, menurut definisi SOA, setiap bagian dari logika bisnis di mana saja terkandung dalam layanan, tetapi sekali lagi, demikian pula seluruh sistem . Layanan dalam SOA dapat memiliki komponen , dan mereka dapat memiliki titik akhir , tetapi cukup berbahaya untuk menyebut setiap bagian dari kode layanan karena bertentangan dengan apa yang seharusnya "S" maksudnya.

    Karena SOA umumnya cukup tertarik pada olahpesan, operasi yang mungkin telah Anda bungkus dalam layanan sebelumnya umumnya dienkapsulasi dalam penangan , tetapi multiplisitasnya berbeda. Setiap pawang menangani satu jenis pesan, satu operasi. Ini adalah interpretasi ketat dari Prinsip Tanggung Jawab Tunggal , tetapi membuat pemeliharaan yang luar biasa karena setiap operasi yang mungkin dilakukan berada di kelasnya sendiri. Jadi Anda tidak benar-benar membutuhkan logika bisnis terpusat, karena perintah mewakili operasi bisnis daripada yang teknis.

Pada akhirnya, dalam arsitektur apa pun yang Anda pilih, akan ada beberapa komponen atau lapisan yang memiliki sebagian besar logika bisnis. Lagi pula, jika logika bisnis tersebar di semua tempat maka Anda hanya memiliki kode spageti. Tetapi apakah Anda menyebut komponen itu suatu layanan , dan bagaimana komponen itu dirancang dalam hal-hal seperti jumlah atau ukuran operasi, tergantung pada tujuan arsitektur Anda.

Tidak ada jawaban benar atau salah, hanya yang berlaku untuk situasi Anda.

Aaronaught
sumber
12
Terima kasih atas jawaban yang sangat terperinci, Anda telah mengklarifikasi semua yang dapat saya pikirkan. Sementara jawaban yang lain juga bagus untuk kualitas yang sangat baik, saya percaya jawaban ini juga cocok untuk semua orang sehingga saya akan menerima yang ini. Saya akan menambahkannya di sini untuk jawaban lain: kualitas dan informasi yang sangat bagus, tetapi sayangnya saya hanya dapat memberi Anda upvote.
Jeroen Vannevel
2
Saya tidak setuju dengan kenyataan bahwa dalam arsitektur layered tradisional, layanan adalah sinonim dengan lapisan logika bisnis.
CodeART
1
@ CodeART: Ini dalam arsitektur 3-tier. Saya telah melihat arsitektur 4-tier di mana terdapat "lapisan aplikasi" antara lapisan presentasi dan bisnis, yang kadang-kadang juga disebut lapisan "layanan", tetapi jujur, satu-satunya tempat yang pernah saya lihat berhasil dilaksanakan dengan sukses adalah perluasan besar. Jalankan produk-produk-seluruh-bisnis-untuk-Anda yang dapat dikonfigurasi secara tak terbatas dari orang-orang seperti SAP atau Oracle, dan saya rasa itu tidak layak disebutkan di sini. Saya dapat menambahkan klarifikasi jika Anda mau.
Aaronaught
1
Tetapi jika kita mengambil 100+ pengontrol garis (misalnya pengontrol menerima pesan - kemudian deserialize objek JSON, buat validasi, terapkan aturan bisnis, simpan ke db, kembalikan objek hasil) dan pindahkan beberapa logika ke salah satu yang disebut metode layanan tidak ' t yang membantu kita secara terpisah menguji setiap bagian tanpa rasa sakit?
artjom
2
@Aaronaught Saya ingin mengklarifikasi satu hal jika kita memiliki objek domain yang dipetakan ke db melalui ORM dan tidak ada logika bisnis di dalamnya apakah ini model domain anemia atau tidak?
artjom
40

Adapun judul Anda , saya pikir pertanyaan itu tidak masuk akal. Model MVC terdiri dari data dan logika bisnis. Mengatakan logika harus dalam Layanan dan bukan Model itu seperti mengatakan, "Penumpang harus duduk di kursi, bukan di dalam mobil".

Kemudian lagi, istilah "Model" adalah istilah yang kelebihan beban. Mungkin Anda tidak bermaksud Model MVC tetapi Anda berarti model dalam arti Data Transfer Object (DTO). AKA dan Entitas. Inilah yang dibicarakan Martin Fowler.

Cara saya melihatnya, Martin Fowler berbicara tentang hal-hal di dunia yang ideal. Dalam dunia nyata Hibernate dan JPA (di tanah Jawa) DTO adalah abstraksi super bocor. Saya ingin menempatkan logika bisnis saya di entitas saya. Itu akan membuat segalanya lebih bersih. Masalahnya adalah entitas ini dapat eksis dalam kondisi terkelola / cache yang sangat sulit untuk dipahami dan terus-menerus mencegah upaya Anda. Untuk meringkas pendapat saya: Martin Fowler merekomendasikan cara yang benar, tetapi ORM mencegah Anda melakukannya.

Saya pikir Bob Martin memiliki saran yang lebih realistis dan dia memberikannya di video ini yang tidak gratis . Dia berbicara tentang menjaga DTO Anda bebas dari logika. Mereka hanya memegang data dan mentransfernya ke lapisan lain yang jauh lebih berorientasi objek dan tidak menggunakan DTO secara langsung. Ini menghindari abstraksi bocor dari menggigit Anda. Lapisan dengan DTO dan DTO sendiri bukan OO. Tapi begitu Anda keluar dari lapisan itu, Anda bisa menjadi sama seperti OO seperti pendukung Martin Fowler.

Manfaat dari pemisahan ini adalah mengabstraksi lapisan ketekunan. Anda bisa beralih dari JPA ke JDBC (atau sebaliknya) dan tidak ada logika bisnis yang harus berubah. Itu hanya tergantung pada DTO, tidak peduli bagaimana DTO tersebut dihuni.

Untuk sedikit mengubah topik, Anda perlu mempertimbangkan fakta bahwa database SQL tidak berorientasi objek. Tapi ORM biasanya memiliki entitas - yang merupakan objek - per tabel. Jadi sejak awal Anda sudah kalah dalam pertempuran. Dalam pengalaman saya, Anda tidak pernah bisa mewakili Entitas dengan cara yang persis Anda inginkan dengan cara yang berorientasi objek.

Adapun " sebuah layanan", Bob Martin akan melawan memiliki kelas bernama FooBarService. Itu tidak berorientasi objek. Apa yang dilakukan layanan? Apa pun yang terkait dengan FooBars. Mungkin juga diberi label FooBarUtils. Saya pikir dia menganjurkan lapisan layanan (nama yang lebih baik akan menjadi lapisan logika bisnis) tetapi setiap kelas di lapisan itu akan memiliki nama yang bermakna.

Daniel Kaplan
sumber
2
Setuju dengan poin Anda tentang ORM; mereka menyebarkan kebohongan bahwa Anda memetakan entitas Anda langsung ke db bersama mereka, padahal dalam kenyataannya suatu entitas dapat disimpan di beberapa tabel.
Andy
@Daniel Kaplan, tahukah Anda apa tautan yang diperbarui untuk video Bob Martin?
Brian Morearty
25

Saya sedang mengerjakan proyek greenfield sekarang dan kami harus membuat beberapa keputusan arsitektur baru kemarin. Lucunya, saya harus meninjau kembali beberapa bab 'Pola Arsitektur Aplikasi Perusahaan'.

Inilah yang kami temukan:

  • Lapisan data. Permintaan dan pembaruan basis data. Lapisan terpapar melalui repositori injeksi.
  • Lapisan domain. Di sinilah logika bisnis hidup. Lapisan ini menggunakan repositori suntik dan bertanggung jawab untuk sebagian besar logika bisnis. Ini adalah inti dari aplikasi yang akan kami uji secara menyeluruh.
  • Lapisan layanan. Lapisan ini berbicara ke lapisan domain dan melayani permintaan klien. Dalam kasus kami, lapisan layanannya cukup sederhana - ia menyampaikan permintaan ke lapisan domain, menangani keamanan dan beberapa masalah lintas sektoral lainnya. Ini tidak jauh berbeda dengan pengontrol dalam aplikasi MVC - pengontrol kecil dan sederhana.
  • Lapisan klien. Berbicara dengan lapisan layanan melalui SOAP.

Kami berakhir dengan yang berikut:

Klien -> Layanan -> Domain -> Data

Kami dapat mengganti klien, layanan atau lapisan data dengan jumlah pekerjaan yang wajar. Jika logika domain Anda tinggal di layanan dan Anda telah memutuskan bahwa Anda ingin mengganti atau bahkan menghapus lapisan layanan Anda, maka Anda harus memindahkan semua logika bisnis di tempat lain. Persyaratan seperti itu jarang terjadi, tetapi itu mungkin terjadi.

Setelah mengatakan semua ini, saya pikir ini cukup dekat dengan apa yang dikatakan Martin Fowler

Layanan ini hidup di atas model domain dan menggunakan model domain untuk data.

Diagram di bawah menggambarkan ini dengan cukup baik:

http://martinfowler.com/eaaCatalog/serviceLayer.html

http://martinfowler.com/eaaCatalog/ServiceLayerSketch.gif

CodeART
sumber
2
Anda memutuskan untuk sabun baru kemarin? Apakah itu syarat atau Anda tidak punya ide yang lebih baik?
JensG
1
REST tidak akan memotongnya untuk persyaratan kami. SABUN atau SISA, ini tidak ada bedanya dengan jawabannya. Dari pemahaman saya, layanan adalah pintu gerbang ke domain logika.
CodeART
Sepenuhnya setuju dengan Gateway. SOAP adalah bloatware (standar), jadi saya harus bertanya. Dan ya, tidak ada dampak pada pertanyaan / jawaban juga.
JensG
6
Bantulah diri Anda sendiri dan bunuh lapisan layanan Anda. UI Anda harus menggunakan domain Anda secara langsung. Saya pernah melihat ini sebelumnya dan domain Anda selalu menjadi sekelompok dpt anemia, bukan model kaya.
Andy
Slide-slide tersebut mencakup penjelasan mengenai lapisan layanan dan grafik di atas cukup rapi: slideshare.net/ShwetaGhate2/…
Marc Juchli
9

Ini adalah salah satu hal yang sangat tergantung pada use case. Titik keseluruhan dari lapisan layanan adalah untuk menggabungkan logika bisnis bersama. Ini berarti bahwa beberapa pengontrol dapat memanggil UserService.MakeHimPay () yang sama tanpa benar-benar peduli tentang bagaimana pembayaran dilakukan. Apa yang terjadi dalam layanan mungkin sesederhana memodifikasi properti objek atau mungkin melakukan logika kompleks berurusan dengan layanan lain (yaitu, memanggil layanan pihak ketiga, memanggil logika validasi, atau bahkan hanya menyimpan sesuatu ke database. )

Ini tidak berarti Anda harus menghapus SEMUA logika dari objek domain. Kadang-kadang lebih masuk akal untuk memiliki metode pada objek domain melakukan beberapa perhitungan pada dirinya sendiri. Dalam contoh terakhir Anda, layanan ini adalah lapisan yang berlebihan di atas objek repositori / domain. Ini memberikan penyangga yang bagus terhadap perubahan persyaratan, tetapi sebenarnya tidak perlu. Jika Anda merasa Anda membutuhkan layanan, cobalah yang melakukan logika "modifikasi properti X pada objek Y" sederhana alih-alih objek domain. Logika pada kelas domain cenderung masuk ke "hitung nilai ini dari bidang" daripada mengekspos semua bidang melalui pengambil / setter.

firelore
sumber
2
Sikap Anda yang mendukung lapisan layanan dengan logika bisnis sangat masuk akal, tetapi ini masih menyisakan beberapa pertanyaan. Dalam posting saya, saya mengutip beberapa sumber terhormat yang berbicara tentang lapisan layanan sebagai façade batal dari logika bisnis. Ini sangat kontras dengan jawaban Anda, bisakah Anda menjelaskan perbedaan ini?
Jeroen Vannevel
5
Saya menemukan bahwa itu benar-benar tergantung pada JENIS logika bisnis dan faktor-faktor lain, seperti bahasa yang digunakan. Beberapa logika bisnis tidak sesuai dengan objek domain dengan sangat baik. Salah satu contohnya adalah memfilter / menyortir hasil setelah menariknya kembali dari database. Ini logika bisnis, tetapi tidak masuk akal pada objek domain. Saya menemukan bahwa layanan yang paling baik digunakan untuk logika sederhana atau mengubah hasil dan logika pada domain paling bermanfaat ketika berurusan dengan menyimpan data atau menghitung data dari objek.
firelore
8

Cara termudah untuk mengilustrasikan mengapa programmer menghindari menempatkan logika domain dalam objek domain adalah bahwa mereka biasanya dihadapkan pada situasi "di mana saya meletakkan logika validasi?" Ambil objek domain ini misalnya:

public class MyEntity
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get { return this.someProperty; }
        set
        {
            if(value < 0) throw new ArgumentOutOfRangeException("value");
            this.someProperty = value;
        }
    }
}

Jadi kami memiliki beberapa logika validasi dasar di setter (tidak boleh negatif). Masalahnya adalah Anda tidak dapat benar-benar menggunakan kembali logika ini. Di suatu tempat ada layar atau ViewModel atau Pengendali yang perlu melakukan validasi sebelum benar-benar melakukan perubahan pada objek domain, karena itu perlu memberi tahu pengguna baik sebelum atau ketika mereka mengklik tombol Simpan bahwa mereka tidak bisa melakukan itu, dan mengapa . Menguji pengecualian ketika Anda memanggil setter adalah hack yang jelek karena Anda benar-benar harus melakukan semua validasi bahkan sebelum Anda memulai transaksi.

Itu sebabnya orang-orang memindahkan logika validasi semacam layanan, seperti MyEntityValidator. Kemudian entitas dan logika panggilan bisa mendapatkan referensi ke layanan validasi dan menggunakannya kembali.

Jika Anda tidak melakukan itu dan Anda masih ingin menggunakan kembali logika validasi, Anda akhirnya meletakkannya dalam metode statis kelas entitas:

public class MyEntity
{
    private int someProperty = 0;

    public int SomeProperty
    {
        get { return this.someProperty; }
        set
        {
            string message;
            if(!TryValidateSomeProperty(value, out message)) 
            {
                throw new ArgumentOutOfRangeException("value", message);
            }
            this.someProperty = value;
        }
    }

    public static bool TryValidateSomeProperty(int value, out string message)
    {
        if(value < 0)
        {
            message = "Some Property cannot be negative.";
            return false;
        }
        message = string.Empty;
        return true;
    }
}

Ini akan membuat model domain Anda kurang "anemia", dan menjaga logika validasi di sebelah properti, yang bagus, tapi saya rasa tidak ada orang yang benar-benar menyukai metode statis.

Scott Whitlock
sumber
1
Jadi apa solusi terbaik untuk validasi menurut pendapat Anda?
Flashrunner
3
@ Flashrunner - logika validasi saya secara definitif ada di lapisan logika bisnis, tetapi juga diduplikasi dalam entitas dan lapisan database dalam beberapa kasus. Lapisan bisnis menanganinya dengan baik dengan memberi tahu pengguna, dll., Tetapi lapisan lain hanya membuang kesalahan / pengecualian dan membuat programmer (saya sendiri) tidak membuat kesalahan yang merusak data.
Scott Whitlock
6

Saya pikir jawabannya jelas jika Anda membaca artikel Model Domain Anemik Martin Fowler .

Menghapus logika bisnis, yang merupakan domain, dari model domain pada dasarnya melanggar desain berorientasi objek.

Mari kita tinjau konsep berorientasi objek paling dasar: Objek merangkum data dan operasi. Misalnya, menutup akun adalah operasi yang harus dilakukan objek objek dengan sendirinya; Oleh karena itu, memiliki lapisan layanan melakukan operasi itu bukan solusi berorientasi objek. Ini prosedural, dan itulah yang dimaksud Martin Fowler ketika dia berbicara tentang model domain anemia.

Jika Anda memiliki lapisan layanan menutup akun, daripada menutup objek akun, Anda tidak memiliki objek akun nyata. "Objek" akun Anda hanyalah struktur data. Apa yang akhirnya Anda dapatkan, seperti yang disarankan Martin Fowler, adalah banyak tas berisi getter dan setter.

Carlos A Merighe - Utah
sumber
1
Diedit. Saya benar-benar menemukan penjelasan yang cukup membantu ini dan tidak menganggapnya layak untuk diturunkan.
BadHorsie
1
Ada satu kelemahan besar dari model kaya. Dan itu adalah pengembang menarik sesuatu yang sedikit terkait dengan model. Apakah status buka / tutup merupakan atribut akun? Bagaimana dengan pemiliknya? Dan banknya? Haruskah mereka semua dirujuk oleh akun? Apakah saya akan menutup akun dengan berbicara ke bank atau melalui akun secara langsung? Dengan model-model anemia, koneksi-koneksi itu bukan bagian inheren dari model-model itu, tetapi lebih diciptakan ketika bekerja dengan model-model itu di kelas lain (sebut saja itu layanan, atau manajer).
Hubert Grzeskowiak
4

Bagaimana Anda menerapkan logika bisnis Anda di lapisan layanan? Ketika Anda melakukan pembayaran dari pengguna, Anda membuat pembayaran, bukan hanya mengurangi nilai dari properti.

Metode pembayaran make Anda perlu membuat catatan pembayaran, menambah hutang pengguna itu dan bertahan semua ini di repositori Anda. Melakukan ini dalam metode layanan sangat mudah, dan Anda juga dapat membungkus seluruh operasi dalam transaksi. Melakukan hal yang sama dalam model domain gabungan jauh lebih bermasalah.

Tuan Cochese
sumber
2

Versi tl; dr:
Pengalaman dan pendapat saya mengatakan bahwa objek apa pun yang memiliki logika bisnis harus menjadi bagian dari model domain. Model data seharusnya tidak memiliki logika apa pun. Layanan mungkin harus menyatukan keduanya, dan menangani masalah lintas sektoral (database, logging, dll.). Namun, jawaban yang diterima adalah yang paling praktis.

Versi yang lebih panjang, yang telah disinggung oleh orang lain, adalah bahwa ada kata penyimpangan pada kata "model". Posting beralih antara model data dan model domain seolah-olah mereka sama, yang merupakan kesalahan yang sangat umum. Mungkin juga ada sedikit penyangkalan pada kata "service".

Secara praktis, Anda seharusnya tidak memiliki layanan yang membuat perubahan pada objek domain apa pun; alasan untuk ini adalah bahwa layanan Anda kemungkinan akan memiliki beberapa metode untuk setiap properti pada objek Anda untuk mengubah nilai properti itu. Ini merupakan masalah karena dengan demikian, jika Anda memiliki antarmuka untuk objek Anda (atau bahkan jika tidak), layanan tidak lagi mengikuti Prinsip Terbuka-Tertutup; sebaliknya, setiap kali Anda menambahkan lebih banyak data ke model Anda (terlepas dari domain vs data), Anda akhirnya harus menambahkan lebih banyak fungsi ke layanan Anda. Ada cara-cara tertentu di sekitarnya, tetapi ini adalah alasan paling umum saya melihat aplikasi "perusahaan" gagal, terutama ketika organisasi-organisasi itu berpikir bahwa "perusahaan" berarti "memiliki antarmuka untuk setiap objek dalam sistem". Bisakah Anda bayangkan menambahkan metode baru ke antarmuka, kemudian ke dua atau tiga implementasi yang berbeda (yang dalam aplikasi, implementasi tiruan, dan debug satu, yang dalam memori?), hanya untuk satu properti pada model Anda? Kedengarannya seperti ide yang buruk bagi saya.

Ada masalah yang lebih lama di sini yang tidak akan saya bahas, tetapi intinya adalah ini: Pemrograman berorientasi objek hardcore mengatakan bahwa tidak ada seorang pun di luar objek yang relevan yang dapat mengubah nilai properti di dalam objek, atau bahkan " lihat "nilai properti di dalam objek. Ini dapat dikurangi dengan membuat data hanya-baca. Anda masih dapat mengalami masalah seperti ketika banyak orang menggunakan data bahkan sebagai hanya baca dan Anda harus mengubah jenis data itu. Mungkin saja semua konsumen harus berubah untuk mengakomodasi itu. Inilah sebabnya, ketika Anda membuat API untuk dikonsumsi oleh siapa saja dan semua orang, Anda disarankan untuk tidak memiliki properti / data publik atau yang dilindungi; itulah alasan utama OOP diciptakan, pada akhirnya.

Saya pikir sebagian besar jawaban di sini, selain dari yang ditandai diterima, semuanya mengaburkan masalah ini. Yang ditandai diterima baik, tetapi saya masih merasa perlu untuk menjawab dan setuju bahwa peluru 4 adalah cara untuk pergi, secara umum.

Dalam DDD, layanan dimaksudkan secara khusus untuk situasi ketika Anda memiliki operasi yang tidak semestinya menjadi milik agregat root. Anda harus berhati-hati di sini, karena seringkali kebutuhan akan layanan dapat menyiratkan bahwa Anda tidak menggunakan akar yang benar. Tetapi dengan asumsi Anda melakukannya, sebuah layanan digunakan untuk mengoordinasikan operasi lintas banyak akar, atau terkadang untuk menangani masalah yang tidak melibatkan model domain sama sekali ...

WolfgangSenff
sumber
1

Jawabannya adalah itu tergantung pada use case. Tetapi dalam sebagian besar skenario umum, saya akan mematuhi logika bisnis yang diletakkan di lapisan layanan. Contoh yang Anda berikan adalah yang sangat sederhana. Namun begitu Anda mulai memikirkan sistem atau layanan yang dipisahkan dan menambahkan perilaku transaksional di atasnya, Anda benar-benar ingin itu terjadi sebagai bagian dari lapisan layanan.

Sumber-sumber yang Anda kutip untuk lapisan layanan tanpa adanya logika bisnis memperkenalkan lapisan lain yang merupakan lapisan bisnis. Dalam banyak skenario lapisan layanan dan lapisan bisnis dikompresi menjadi satu. Itu sangat tergantung pada bagaimana Anda ingin merancang sistem Anda. Anda bisa menyelesaikan pekerjaan dalam tiga lapisan dan terus mendekorasi dan menambahkan kebisingan.

Apa yang Anda idealnya dapat lakukan adalah memodelkan layanan yang mencakup logika bisnis untuk bekerja pada model domain untuk mempertahankan status . Anda harus mencoba memisahkan layanan sebanyak mungkin.

cerah
sumber
0

Dalam MVC Model didefinisikan sebagai logika bisnis. Mengklaim bahwa itu harus di tempat lain tidak benar kecuali dia tidak menggunakan MVC. Saya melihat lapisan layanan mirip dengan sistem modul. Ini memungkinkan Anda untuk menggabungkan satu set fungsionalitas terkait ke dalam paket yang bagus. Internal lapisan layanan itu akan memiliki model melakukan pekerjaan yang sama seperti milik Anda.

Model terdiri dari data aplikasi, aturan bisnis, logika, dan fungsi. http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller

stonemetal
sumber
0

Konsep lapisan Layanan dapat dilihat dari perspektif DDD. Aaronaught menyebutkannya dalam jawabannya, saya hanya sedikit menguraikannya.

Pendekatan umum ada untuk memiliki pengontrol yang spesifik untuk jenis klien. Katakanlah, ini bisa berupa browser web, bisa juga beberapa aplikasi lain, bisa juga tes fungsional. Format permintaan dan respons mungkin beragam. Jadi saya menggunakan layanan aplikasi sebagai alat untuk memanfaatkan arsitektur Hexagonal . Saya menyuntikkan kelas infrastruktur di sana khusus untuk permintaan konkret. Misalnya, begitulah tampilan pengontrol saya yang melayani permintaan browser web:

class WebBroserController
{
    public function purchaseOrder()
    {
        $data = $_POST;

        $responseData =
            new PurchaseOrderApplicationService(
                new PayPalClient(),
                new OrderRepository()
            )
        ;

        $response = new HtmlView($responseData);
    }
}

Jika saya menulis tes fungsional, saya ingin menggunakan klien pembayaran palsu dan mungkin saya tidak akan memerlukan respons html. Jadi pengontrol saya bisa terlihat seperti itu:

class FunctionalTestController
{
    public function purchaseOrder()
    {
        $data = $_POST;

        $responseData =
            new PurchaseOrderApplicationService(
                new FakePayPalClient(),
                new OrderRepository(),
                $data
            )
        ;

        return new JsonData($responseData);
    }
}

Jadi layanan aplikasi adalah lingkungan yang saya atur untuk menjalankan logika bisnis. Di situlah kelas model disebut - agnostik dari implementasi infrastruktur.

Jadi, jawablah pertanyaan Anda dari perspektif ini:

Apakah ini sarana untuk mengekstrak logika dari controller dan memasukkannya ke dalam layanan?

Tidak.

Apakah ini seharusnya membentuk kontrak antara pengontrol dan domain?

Yah, orang bisa menyebutnya begitu.

Haruskah ada lapisan antara domain dan lapisan layanan?

Nggak.


Namun ada pendekatan yang sangat berbeda yang sama sekali menyangkal penggunaan segala jenis layanan. Misalnya, David West dalam bukunya Object Thinking mengklaim bahwa objek apa pun harus memiliki semua sumber daya yang diperlukan untuk melakukan tugasnya. Pendekatan ini menghasilkan, misalnya, membuang ORM .

Zapadlo
sumber
-2

Untuk catatan.

SRP:

  1. Model = Data, ini dia setter dan getter.
  2. Logika / Layanan = inilah keputusannya.
  3. Repositori / DAO = di sini kami mengizinkan menyimpan atau mengambil informasi.

Dalam hal ini, boleh-boleh saja melakukan langkah-langkah selanjutnya:

Jika hutang tidak membutuhkan perhitungan:

userObject.Debt = 9999;

Namun, jika memerlukan beberapa perhitungan maka:

userObject.Debt= UserService.CalculateDebt(userObject)

atau juga

UserService.UpdateDebt(userObject)

Tetapi juga, jika perhitungan dilakukan dalam lapisan persistensi, prosedur penyimpanan seperti itu

UserRepository.UpdateDebt(userObject)

Dalam hal ini, saya ingin mengambil pengguna dari basis data dan memperbarui utangnya kemudian, kita harus melakukannya dalam beberapa langkah (sebenarnya, dua) dan tidak perlu membungkus / merangkumnya dalam fungsi layanan.

User userObject=UserRepository.GetUserByName(somename);
UserService.UpdateDebt(userObject)

Dan jika perlu menyimpannya, kita bisa menambahkan langkah ketiga

User userObject=UserRepository.GetUserByName(somename);
UserService.UpdateDebt(userObject)
UserRepository.Save(userobject);

Tentang solusi yang diajukan

a) Kita tidak perlu takut meninggalkan pengembang akhir untuk menulis beberapa alih-alih merangkumnya dalam suatu fungsi.

b) Dan tentang Antarmuka, beberapa pengembang menyukai antarmuka dan mereka baik-baik saja tetapi dalam beberapa kasus, mereka tidak perlu sama sekali.

c) Tujuan dari suatu layanan adalah untuk membuat satu tanpa atribut, terutama karena kita dapat menggunakan fungsi Shared / Static. Ini juga mudah untuk unit test.

magallanes
sumber
bagaimana ini menjawab pertanyaan yang diajukan: Seberapa akurat "Logika bisnis harus dalam layanan, bukan dalam model"?
nyamuk
3
Kalimat macam apa itu "We shouldn't be afraid to left the end-developer to write a couple of instead of encapsulate it in a function."? Saya hanya bisa mengutip Lewis Black" jika bukan karena kuda saya, saya tidak akan menghabiskan tahun itu di perguruan tinggi ".
Maleakhi