สถานการณ์
เมื่อเช้านี้ฉันตอบคำถามใน StackOverflow
คำถาม:
การแก้ไขวัตถุที่มีอยู่ควรทำในพื้นที่เก็บข้อมูลเลเยอร์หรือบริการ?
ตัวอย่างเช่นถ้าฉันมีผู้ใช้ที่มีหนี้ ฉันต้องการเปลี่ยนหนี้ของเขา ฉันควรทำมันใน UserRepository หรือในการให้บริการเช่น BuyingService โดยรับวัตถุแก้ไขและบันทึกมันได้หรือไม่
คำตอบของฉัน:
คุณควรปล่อยให้ความรับผิดชอบในการกลายวัตถุเป็นวัตถุเดียวกันนั้นและใช้ที่เก็บเพื่อดึงข้อมูลวัตถุนี้
สถานการณ์ตัวอย่าง:
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
}
}
ความคิดเห็นที่ฉันได้รับ:
ตรรกะทางธุรกิจควรอยู่ในบริการจริงๆ ไม่ได้อยู่ในรูปแบบ
อินเทอร์เน็ตพูดว่าอะไร?
ดังนั้นสิ่งนี้ทำให้ฉันค้นหาเนื่องจากฉันไม่เคยใช้บริการชั้น (จริงๆแล้ว) ฉันเริ่มอ่านเกี่ยวกับรูปแบบ Service Layer และรูปแบบของหน่วยงาน แต่จนถึงตอนนี้ฉันไม่สามารถพูดได้ว่าฉันเชื่อว่าต้องใช้ชั้นบริการ
ยกตัวอย่างเช่นบทความนี้โดย Martin Fowler เกี่ยวกับรูปแบบการต่อต้านของ Anemic Domain Model:
มีวัตถุหลายชื่อหลังจากคำนามในพื้นที่โดเมนและวัตถุเหล่านี้เชื่อมต่อกับความสัมพันธ์ที่หลากหลายและโครงสร้างที่แบบจำลองโดเมนที่แท้จริงมี การจับมาเมื่อคุณดูที่พฤติกรรมและคุณรู้ว่าแทบจะไม่มีพฤติกรรมใด ๆ บนวัตถุเหล่านี้ทำให้พวกมันเล็กกว่าถุงเกทเทอร์และเซทเตอร์ บ่อยครั้งที่โมเดลเหล่านี้มาพร้อมกับกฎการออกแบบที่บอกว่าคุณจะไม่ใส่ตรรกะโดเมนใด ๆ ในวัตถุโดเมน มีชุดของวัตถุบริการที่จับตรรกะโดเมนทั้งหมดแทน บริการเหล่านี้อยู่บนสุดของโมเดลโดเมนและใช้โมเดลโดเมนสำหรับข้อมูล
(... ) ตรรกะที่ควรอยู่ในวัตถุโดเมนคือตรรกะของโดเมน - การตรวจสอบการคำนวณกฎเกณฑ์ทางธุรกิจ - สิ่งที่คุณต้องการเรียก
สำหรับฉันนี่ดูเหมือนว่าสถานการณ์เป็นเรื่องเกี่ยวกับ: ฉันสนับสนุนการจัดการข้อมูลของวัตถุโดยการแนะนำวิธีการภายในชั้นเรียนที่ทำเช่นนั้น อย่างไรก็ตามฉันตระหนักดีว่านี่ควรเป็นวิธีใดวิธีหนึ่งและอาจมีความเกี่ยวข้องกับวิธีการที่เรียกใช้ (ใช้ที่เก็บ)
ฉันยังมีความรู้สึกว่าในบทความนั้น (ดูด้านล่าง) ซึ่งเป็นชั้นบริการถือว่ามากขึ้นเป็นfaçadeที่ได้รับมอบหมายทำงานกับรูปแบบพื้นฐานกว่าชั้นการทำงานอย่างหนักที่เกิดขึ้นจริง
Application Layer [ชื่อของเขาสำหรับ Service Layer]: กำหนดงานที่ซอฟต์แวร์ควรทำและชี้นำวัตถุโดเมนที่แสดงออกเพื่อแก้ปัญหา ภารกิจที่เลเยอร์นี้รับผิดชอบนั้นมีความหมายต่อธุรกิจหรือจำเป็นสำหรับการโต้ตอบกับเลเยอร์แอปพลิเคชันของระบบอื่น เลเยอร์นี้เก็บไว้บาง ๆ มันไม่ได้มีกฎเกณฑ์ทางธุรกิจหรือความรู้ แต่เพียงประสานงานและผู้รับมอบสิทธิ์ในการทำงานร่วมกันของวัตถุโดเมนในชั้นถัดไปลง ไม่มีสถานะที่สะท้อนสถานการณ์ธุรกิจ แต่สามารถมีสถานะที่แสดงถึงความคืบหน้าของงานสำหรับผู้ใช้หรือโปรแกรม
ซึ่งเสริมที่นี่ :
ส่วนต่อประสานบริการ บริการเปิดเผยส่วนต่อประสานบริการที่ส่งข้อความขาเข้าทั้งหมด คุณสามารถนึกถึงส่วนต่อประสานการให้บริการในรูปแบบซุ้มที่เผยให้เห็นถึงตรรกะทางธุรกิจที่นำมาใช้ในแอปพลิเคชัน (โดยทั่วไปแล้วคือตรรกะในชั้นธุรกิจ) กับผู้บริโภค
และที่นี่ :
เลเยอร์บริการควรปราศจากแอปพลิเคชันหรือตรรกะทางธุรกิจใด ๆ และควรให้ความสำคัญกับข้อกังวลเล็กน้อยเป็นหลัก ควรตัดการเรียกใช้เลเยอร์ธุรกิจแปลโดเมนของคุณเป็นภาษากลางที่ลูกค้าของคุณสามารถเข้าใจและจัดการสื่อการสื่อสารระหว่างเซิร์ฟเวอร์และไคลเอนต์ที่ร้องขอ
นี่คือความแตกต่างที่ร้ายแรงกับทรัพยากรอื่น ๆที่พูดถึง Service Layer:
เลเยอร์บริการควรประกอบด้วยคลาสที่มีเมธอดที่เป็นหน่วยของการทำงานกับการกระทำที่เป็นของธุรกรรมเดียวกัน
หรือคำตอบที่สองสำหรับคำถามที่ฉันเชื่อมโยงแล้ว:
ในบางจุดแอปพลิเคชันของคุณจะต้องการตรรกะทางธุรกิจบางอย่าง นอกจากนี้คุณอาจต้องการตรวจสอบความถูกต้องของอินพุตเพื่อให้แน่ใจว่าไม่มีสิ่งใดที่ชั่วร้ายหรือไม่ได้รับการร้องขอ ตรรกะนี้อยู่ในเลเยอร์บริการของคุณ
"วิธีการแก้"?
ทำตามคำแนะนำในคำตอบนี้ฉันพบกับวิธีการต่อไปนี้ที่ใช้ Service Layer:
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;
}
}
ข้อสรุป
ทั้งหมดมารวมกันไม่มากมีการเปลี่ยนแปลงที่นี่: รหัสจากคอนโทรลเลอร์ได้ย้ายไปยังชั้นบริการ (ซึ่งเป็นสิ่งที่ดีดังนั้นจึงมีส่วนต่างจากวิธีนี้) อย่างไรก็ตามสิ่งนี้ดูเหมือนจะไม่เกี่ยวข้องกับคำตอบดั้งเดิมของฉัน
ฉันตระหนักถึงรูปแบบการออกแบบเป็นแนวทางไม่ใช่กฎที่กำหนดไว้ในหินที่จะนำไปปฏิบัติเมื่อเป็นไปได้ ถึงกระนั้นฉันยังไม่พบคำอธิบายที่ชัดเจนเกี่ยวกับเลเยอร์บริการและวิธีที่ควรพิจารณา
มันเป็นวิธีการเพียงดึงตรรกะจากตัวควบคุมและใส่ไว้ในบริการแทนหรือไม่
มันควรจะเป็นสัญญาระหว่างตัวควบคุมและโดเมนหรือไม่
ควรมีเลเยอร์ระหว่างโดเมนกับเลเยอร์บริการหรือไม่
และสุดท้าย แต่ไม่ท้ายสุด: ทำตามความคิดเห็นดั้งเดิม
ตรรกะทางธุรกิจควรอยู่ในบริการจริงๆ ไม่ได้อยู่ในรูปแบบ
ถูกต้องหรือไม่
- ฉันจะแนะนำตรรกะทางธุรกิจของฉันในบริการแทนรุ่นได้อย่างไร