“ ตรรกะทางธุรกิจควรอยู่ในบริการไม่แม่นยำในแบบจำลอง”


397

สถานการณ์

เมื่อเช้านี้ฉันตอบคำถามใน 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;
    }
}

ข้อสรุป

ทั้งหมดมารวมกันไม่มากมีการเปลี่ยนแปลงที่นี่: รหัสจากคอนโทรลเลอร์ได้ย้ายไปยังชั้นบริการ (ซึ่งเป็นสิ่งที่ดีดังนั้นจึงมีส่วนต่างจากวิธีนี้) อย่างไรก็ตามสิ่งนี้ดูเหมือนจะไม่เกี่ยวข้องกับคำตอบดั้งเดิมของฉัน

ฉันตระหนักถึงรูปแบบการออกแบบเป็นแนวทางไม่ใช่กฎที่กำหนดไว้ในหินที่จะนำไปปฏิบัติเมื่อเป็นไปได้ ถึงกระนั้นฉันยังไม่พบคำอธิบายที่ชัดเจนเกี่ยวกับเลเยอร์บริการและวิธีที่ควรพิจารณา

  • มันเป็นวิธีการเพียงดึงตรรกะจากตัวควบคุมและใส่ไว้ในบริการแทนหรือไม่

  • มันควรจะเป็นสัญญาระหว่างตัวควบคุมและโดเมนหรือไม่

  • ควรมีเลเยอร์ระหว่างโดเมนกับเลเยอร์บริการหรือไม่

และสุดท้าย แต่ไม่ท้ายสุด: ทำตามความคิดเห็นดั้งเดิม

ตรรกะทางธุรกิจควรอยู่ในบริการจริงๆ ไม่ได้อยู่ในรูปแบบ

  • ถูกต้องหรือไม่

    • ฉันจะแนะนำตรรกะทางธุรกิจของฉันในบริการแทนรุ่นได้อย่างไร

6
ฉันปฏิบัติต่อเลเยอร์บริการเป็นสถานที่ที่จะทำให้ส่วนที่ไม่สามารถหลีกเลี่ยงได้ของสคริปต์ธุรกรรมที่ดำเนินการตามรากรวม ถ้าเลเยอร์บริการของฉันซับซ้อนเกินไปที่ส่งสัญญาณว่าฉันเคลื่อนไหวในทิศทางของโมเดลโรคโลหิตจางและโมเดลโดเมนของฉันต้องการความสนใจและทบทวน ฉันพยายามใส่ตรรกะใน SL ซึ่งไม่ซ้ำซ้อนกับธรรมชาติของมัน
Pavel Voronin

ฉันคิดว่าบริการเป็นส่วนหนึ่งของเลเยอร์โมเดล ฉันคิดผิดหรือเปล่า
Florian Margaine

ฉันใช้กฎง่ายๆ: อย่าพึ่งสิ่งที่คุณไม่ต้องการ; ไม่เช่นนั้นฉันอาจได้รับผลกระทบจากการเปลี่ยนแปลงในส่วนที่ฉันไม่ต้องการ ผลที่ตามมาฉันจบลงด้วยบทบาทที่กำหนดไว้อย่างชัดเจนจำนวนมาก วัตถุข้อมูลของฉันมีพฤติกรรมตราบเท่าที่ลูกค้าทั้งหมดใช้ มิฉะนั้นฉันจะย้ายพฤติกรรมในชั้นเรียนการใช้บทบาทที่จำเป็น
beluchin

1
ตรรกะการควบคุมการไหลของแอปพลิเคชันอยู่ในคอนโทรลเลอร์ ตรรกะการเข้าถึงข้อมูลอยู่ในที่เก็บ ตรรกะการตรวจสอบความถูกต้องอยู่ในเลเยอร์บริการ เซอร์วิสเลเยอร์เป็นเลเยอร์เพิ่มเติมในแอปพลิเคชัน ASP.NET MVC ที่เป็นตัวกลางในการสื่อสารระหว่างคอนโทรลเลอร์และเลเยอร์ที่เก็บข้อมูล ชั้นบริการมีตรรกะการตรวจสอบทางธุรกิจ กรุ asp.net/mvc/overview/older-versions-1/models-data/ …
Kbdavis07

3
IMHO รูปแบบโดเมน OOP ที่ถูกต้องควรหลีกเลี่ยง "บริการธุรกิจ" และรักษาตรรกะทางธุรกิจไว้ในแบบจำลอง แต่ในทางปฏิบัติแสดงให้เห็นว่ามันดึงดูดมากที่จะสร้างเลเยอร์ธุรกิจขนาดใหญ่ที่มีวิธีการตั้งชื่อที่แตกต่างกันและการทำธุรกรรม (หรือหน่วยงาน) รอบวิธีทั้งหมด ง่ายกว่ามากในการประมวลผลคลาสโมเดลในวิธีการบริการทางธุรกิจเดียวมากกว่าการวางแผนความสัมพันธ์ระหว่างคลาสโมเดลเหล่านั้นเพราะคุณจะมีคำถามที่ยากต่อการตอบ: รูตรวมอยู่ที่ไหน ฉันควรเริ่มต้น / กระทำธุรกรรมฐานข้อมูลที่ไหน? ฯลฯ
JustAMartin

คำตอบ:


368

ในการกำหนดความรับผิดชอบของบริการคุณต้องกำหนดว่าบริการคืออะไร

บริการไม่ใช่คำที่ยอมรับได้หรือเป็นซอฟต์แวร์ทั่วไป ในความเป็นจริงต่อท้ายServiceชื่อชั้นเรียนเป็นจำนวนมากเหมือนมาก maligned ผู้จัดการ : มันจะบอกคุณเกือบไม่มีอะไรเกี่ยวกับสิ่งที่วัตถุจริงไม่

ในความเป็นจริงสิ่งที่บริการที่ควรทำคือสถาปัตยกรรมเฉพาะสูง:

  1. ในชั้นสถาปัตยกรรมแบบดั้งเดิมบริการเป็นอักษรตรงกันกับชั้นตรรกะทางธุรกิจ เป็นเลเยอร์ระหว่าง UI และข้อมูล ดังนั้นกฎเกณฑ์ทางธุรกิจทั้งหมดจะเข้าสู่บริการ ชั้นข้อมูลควรเข้าใจการดำเนินการ CRUD ขั้นพื้นฐานเท่านั้นและชั้น UI ควรจัดการเฉพาะกับการทำแผนที่ของงานนำเสนอ DTOs ไปยังและจากวัตถุธุรกิจ

  2. ในสถาปัตยกรรม RPC แบบกระจาย (SOAP, UDDI, BPEL ฯลฯ ) การบริการเป็นรุ่นตรรกะของร่างกายปลายทาง เป็นชุดของการดำเนินงานที่ผู้ดูแลต้องการให้เป็น API สาธารณะ แนวทางปฏิบัติที่ดีที่สุดหลายข้ออธิบายว่าการปฏิบัติการบริการควรจริงแล้วเป็นการดำเนินงานระดับธุรกิจไม่ใช่ CRUD และฉันมักจะเห็นด้วย

    อย่างไรก็ตามเนื่องจากการกำหนดเส้นทางทุกอย่างผ่านบริการระยะไกลที่เกิดขึ้นจริงอาจส่งผลกระทบต่อประสิทธิภาพการทำงานอย่างจริงจังจึงเป็นเรื่องดีที่สุดที่จะไม่ให้บริการเหล่านี้ใช้ตรรกะทางธุรกิจด้วยตนเอง พวกเขาควรห่อชุดของวัตถุภายในไว้ภายใน บริการเดียวอาจเกี่ยวข้องกับวัตถุทางธุรกิจหนึ่งหรือหลายรายการ

  3. ในสถาปัตยกรรม MVP / MVC / MVVM / MV * บริการจะไม่มีอยู่เลย หรือถ้าพวกเขาทำคำที่ใช้ในการอ้างถึงวัตถุทั่วไปใด ๆ ที่สามารถฉีดเข้าไปในตัวควบคุมหรือดูรูปแบบ ตรรกะทางธุรกิจของคุณอยู่ในรูปแบบ หากคุณต้องการสร้าง "บริการวัตถุ" เพื่อจัดการการดำเนินงานที่ซับซ้อนนั่นจะถูกมองว่าเป็นรายละเอียดการใช้งาน ผู้คนจำนวนมากเศร้านำ MVC มาใช้เช่นนี้ แต่ก็ถือว่าเป็นรูปแบบการต่อต้าน ( Anemic Domain Model ) เพราะรูปแบบตัวเองไม่ได้ทำอะไรเลยมันเป็นเพียงคุณสมบัติมากมายสำหรับ UI

    บางคนคิดผิดพลาดว่าการใช้วิธีควบคุม 100 บรรทัดและผลักมันทั้งหมดไปสู่การบริการเพื่อสร้างสถาปัตยกรรมที่ดีขึ้น มันไม่ได้จริงๆ สิ่งที่มันทำคือการเพิ่มอีกชั้นที่ไม่จำเป็นอาจเป็นทางอ้อม ในทางปฏิบัติตัวควบคุมยังคงทำงานอยู่มันแค่ทำผ่านวัตถุ "ตัวช่วย" ที่มีชื่อไม่ดี ฉันขอแนะนำรูปแบบWicked Domain Modelsของ Jimmy Bogard เพื่อเป็นตัวอย่างที่ชัดเจนของวิธีเปลี่ยนรูปแบบโดเมน anemic ให้เป็นประโยชน์ มันเกี่ยวข้องกับการตรวจสอบอย่างรอบคอบของแบบจำลองที่คุณเปิดเผยและการดำเนินงานที่ถูกต้องจริงในบริบททางธุรกิจ

    ตัวอย่างเช่นหากฐานข้อมูลของคุณมีคำสั่งซื้อและคุณมีคอลัมน์สำหรับจำนวนเงินรวมใบสมัครของคุณอาจไม่ได้รับอนุญาตให้เปลี่ยนฟิลด์นั้นเป็นค่าที่กำหนดเองได้เนื่องจาก (a) ประวัติและ (b) มันควรจะเป็น พิจารณาจากสิ่งที่อยู่ในคำสั่งรวมถึงข้อมูล / กฎที่ไวต่อเวลาอื่น ๆ การสร้างบริการเพื่อจัดการคำสั่งซื้อไม่จำเป็นต้องแก้ปัญหานี้เพราะรหัสผู้ใช้ยังคงสามารถจับวัตถุคำสั่งซื้อที่เกิดขึ้นจริงและเปลี่ยนจำนวนเงินในมัน คำสั่งนั้นควรรับผิดชอบในการทำให้มั่นใจว่าสามารถเปลี่ยนแปลงได้ในวิธีที่ปลอดภัยและสอดคล้องกันเท่านั้น

  4. ใน DDD บริการมีความหมายเฉพาะสำหรับสถานการณ์เมื่อคุณมีการดำเนินการที่ไม่ถูกต้องอยู่ในรากรวมใดคุณต้องระวังที่นี่เพราะบ่อยครั้งที่ความต้องการบริการสามารถบ่งบอกได้ว่าคุณไม่ได้ใช้รากที่ถูกต้อง แต่สมมติว่าคุณทำบริการใช้ในการประสานงานการดำเนินงานข้ามหลายรูตหรือบางครั้งเพื่อจัดการกับข้อกังวลที่ไม่เกี่ยวข้องกับโมเดลโดเมนเลย (เช่นอาจจะเขียนข้อมูลไปยังฐานข้อมูล BI / OLAP)

    แง่มุมหนึ่งที่โดดเด่นของบริการ DDD ก็คือว่ามันได้รับอนุญาตให้ใช้สคริปต์การทำธุรกรรม เมื่อทำงานกับแอพพลิเคชั่นขนาดใหญ่ในที่สุดคุณก็จะพบเจอกับอินสแตนซ์ที่มันเป็นวิธีที่ง่ายกว่าที่จะทำอะไรบางอย่างด้วยโพรซีเดอร์ T-SQL หรือ PL / SQL มากกว่าที่จะยุ่งยากกับโมเดลโดเมน นี่คือตกลงและมันเป็นของบริการ

    นี่คือการออกเดินทางที่รุนแรงจากคำจำกัดความของสถาปัตยกรรมการบริการ เซอร์วิสเลเยอร์ encapsulate วัตถุโดเมน; บริการ DDD สรุปสิ่งที่ไม่ได้อยู่ในวัตถุโดเมนและไม่สมเหตุสมผล

  5. ในสถาปัตยกรรมที่มุ่งเน้นการบริการการบริการนั้นถือเป็นสิทธิ์ทางเทคนิคสำหรับความสามารถทางธุรกิจ นั่นหมายความว่ามันเป็นเจ้าของแต่เพียงผู้เดียวของชุดย่อยบางส่วนของข้อมูลธุรกิจและไม่มีอะไรที่ได้รับอนุญาตให้สัมผัสกับข้อมูลนั้น - แม้กระทั่งเพียงแค่อ่านมัน

    ตามความจำเป็นการบริการเป็นข้อเสนอแบบ end-to-end ใน SOA ความหมายบริการไม่ได้มีส่วนประกอบเฉพาะเท่าทั้งกองซ้อนและแอปพลิเคชันทั้งหมดของคุณ (หรือธุรกิจทั้งหมดของคุณ) เป็นชุดของบริการเหล่านี้ที่ทำงานพร้อมกันโดยไม่มีจุดตัดยกเว้นที่เลเยอร์การส่งข้อความและ UI แต่ละบริการมีข้อมูลของตัวเองกฎเกณฑ์ทางธุรกิจและ UI ของตัวเอง พวกเขาไม่จำเป็นต้องประสานงานกันเพราะพวกเขาควรจะเป็นแนวธุรกิจ - และเช่นเดียวกับธุรกิจตัวเองแต่ละบริการมีชุดของความรับผิดชอบของตนเองและดำเนินการมากหรือน้อยเป็นอิสระจากคนอื่น ๆ

    ดังนั้นโดยความหมาย SOA ชิ้นส่วนของตรรกะทางธุรกิจทุกทุกที่ที่มีอยู่ในบริการ แต่แล้วอีกครั้งเพื่อให้เป็นทั้งระบบ บริการใน SOA สามารถมีส่วนประกอบและพวกเขาสามารถมีปลายทางแต่มันอันตรายพอสมควรที่จะเรียกชิ้นส่วนของรหัสใด ๆบริการเพราะขัดแย้งกับสิ่งเดิม "S" ควรจะหมายถึง

    เนื่องจากโดยทั่วไป SOA นั้นค่อนข้างกระตือรือร้นในการรับส่งข้อความการดำเนินการที่คุณอาจทำแพ็กเกจในเซอร์วิสก่อนโดยทั่วไปจะถูกห่อหุ้มในตัวจัดการแต่หลายหลากแตกต่างกัน แต่ละจัดการจับหนึ่งประเภทข้อความหนึ่งในการดำเนินงาน เป็นการตีความที่เข้มงวดของหลักการความรับผิดชอบเดี่ยวแต่ทำให้มีการบำรุงรักษาที่ดีเพราะการดำเนินการที่เป็นไปได้ทุกอย่างอยู่ในระดับเดียวกัน ดังนั้นคุณจึงไม่จำเป็นต้องใช้ตรรกะทางธุรกิจแบบรวมศูนย์เพราะคำสั่งแสดงถึงการดำเนินธุรกิจมากกว่าที่เป็นทางเทคนิค

ในที่สุดสถาปัตยกรรมที่คุณเลือกจะมีองค์ประกอบหรือเลเยอร์ที่มีตรรกะทางธุรกิจเกือบทั้งหมด ท้ายที่สุดถ้าตรรกะทางธุรกิจกระจัดกระจายไปทั่วสถานที่แล้วคุณก็มีรหัสสปาเก็ตตี้ แต่ไม่ว่าคุณจะเรียกใช้บริการนั้นหรือไม่และองค์ประกอบนั้นได้รับการออกแบบในแง่ของสิ่งต่าง ๆ เช่นจำนวนหรือขนาดของการดำเนินการขึ้นอยู่กับเป้าหมายทางสถาปัตยกรรมของคุณ

ไม่มีคำตอบที่ถูกหรือผิดมีเพียงสิ่งที่นำไปใช้กับสถานการณ์ของคุณ


12
ขอบคุณสำหรับคำตอบที่ซับซ้อนมากคุณได้ชี้แจงสิ่งที่ฉันคิด ในขณะที่คำตอบอื่น ๆ นั้นดีต่อคุณภาพที่ยอดเยี่ยมเช่นกันฉันเชื่อว่าคำตอบนี้จะตอบโจทย์พวกเขาทั้งหมดดังนั้นฉันจะยอมรับคำตอบนี้ ฉันจะเพิ่มที่นี่สำหรับคำตอบอื่น ๆ : คุณภาพและข้อมูลที่สวยงาม แต่น่าเศร้าที่ฉันจะสามารถให้ upvote แก่คุณเท่านั้น
Jeroen Vannevel

2
ฉันไม่เห็นด้วยกับความจริงที่ว่าในสถาปัตยกรรมเลเยอร์แบบดั้งเดิมการบริการเป็นคำพ้องกับเลเยอร์ตรรกะทางธุรกิจ
CodeART

1
@CodeART: มันอยู่ในสถาปัตยกรรม 3 ชั้น ฉันได้เห็นสถาปัตยกรรม 4 ชั้นที่มี "แอพพลิเคชั่นเลเยอร์" ระหว่างการนำเสนอและชั้นธุรกิจซึ่งบางครั้งเรียกว่าเลเยอร์ "บริการ" แต่โดยสุจริตแล้วสถานที่เดียวที่ฉันเคยเห็นการติดตั้งใช้งานนี้ประสบความสำเร็จนั้น กำหนดค่าได้อย่างเต็มที่สำหรับธุรกิจของคุณสำหรับผลิตภัณฑ์ของคุณจาก SAP หรือ Oracle และฉันไม่คิดว่ามันคุ้มค่าที่จะพูดถึงที่นี่ ฉันสามารถเพิ่มคำอธิบายหากคุณต้องการ
Aaronaught

1
แต่ถ้าเราใช้คอนโทรลเลอร์มากกว่า 100+ ตัว (ตัวอย่างเช่นคอนโทรลเลอร์ยอมรับข้อความ - จากนั้นยกเลิกการทำให้วัตถุ JSON ทำการตรวจสอบ, ใช้กฎธุรกิจ, บันทึกเป็น db, ส่งคืนวัตถุผลลัพธ์) และย้ายตรรกะไปยังหนึ่งในวิธีการบริการที่เรียกว่า ' ที่ช่วยให้เราแยกหน่วยทดสอบแต่ละส่วนอย่างเจ็บปวดหรือไม่?
artjom

2
@Aaraught ฉันต้องการชี้แจงสิ่งหนึ่งถ้าเรามีวัตถุโดเมนแมปไปยังฐานข้อมูลผ่านทาง ORM และไม่มีตรรกะทางธุรกิจในพวกเขาเป็นรูปแบบโดเมน anemic นี้หรือไม่?
artjom

40

สำหรับชื่อของคุณฉันไม่คิดว่าคำถามจะสมเหตุสมผล โมเดล MVC ประกอบด้วยข้อมูลและตรรกะทางธุรกิจ การบอกเหตุผลควรอยู่ในบริการไม่ใช่แบบจำลองเหมือนพูดว่า "ผู้โดยสารควรนั่งในที่นั่งไม่ใช่ในรถ"

จากนั้นอีกครั้งคำว่า "Model" เป็นคำที่ใช้มากเกินไป บางทีคุณอาจไม่ได้หมายถึงโมเดล MVC แต่คุณหมายถึงโมเดลในแง่ของ Data Transfer Object (DTO) AKA เป็นเอนทิตี นี่คือสิ่งที่ Martin Fowler กำลังพูดถึง

วิธีที่ฉันเห็นมาร์ตินฟาวเลอร์กำลังพูดถึงสิ่งต่าง ๆ ในโลกอุดมคติ ในโลกแห่งความเป็นจริงของ Hibernate และ JPA (ในดินแดน Java) DTO นั้นเป็นสิ่งที่เป็นนามธรรมอย่างยิ่ง ฉันชอบที่จะใส่ตรรกะทางธุรกิจของฉันในเอนทิตีของฉัน มันจะทำให้ทุกอย่างสะอาดขึ้น ปัญหาคือเอนทิตีเหล่านี้สามารถมีอยู่ในสถานะที่ถูกจัดการ / แคชซึ่งยากต่อการเข้าใจและป้องกันความพยายามของคุณอย่างต่อเนื่อง เพื่อสรุปความคิดเห็นของฉัน: Martin Fowler แนะนำวิธีที่ถูกต้อง แต่ ORMs ป้องกันไม่ให้คุณทำ

ผมคิดว่าบ๊อบมาร์ตินมีข้อเสนอแนะที่มีเหตุผลมากขึ้นและเขาจะช่วยให้มันในวิดีโอนี้ว่าไม่ฟรี เขาพูดถึงการรักษา DTO ของคุณโดยไม่มีเหตุผล พวกเขาเพียงแค่เก็บข้อมูลและถ่ายโอนไปยังเลเยอร์อื่นที่มีวัตถุมากขึ้นและไม่ได้ใช้ DTO โดยตรง วิธีนี้จะช่วยป้องกันไม่ให้สิ่งที่เป็นนามธรรมรั่วไหลกัดคุณ เลเยอร์ที่มี DTO และ DTO นั้นไม่ใช่ OO แต่เมื่อคุณออกจากเลเยอร์นั้นคุณจะต้องเป็น OO เหมือนที่ Martin Fowler สนับสนุน

ประโยชน์ที่ได้รับจากการแยกนี้คือมันแยกชั้นความเพียร คุณสามารถเปลี่ยนจาก JPA เป็น JDBC (หรือกลับกัน) และไม่มีตรรกะทางธุรกิจที่จะต้องเปลี่ยน มันขึ้นอยู่กับ DTOs มันไม่สนใจว่า DTO เหล่านั้นจะมีประชากรอย่างไร

ในการเปลี่ยนหัวข้อเล็กน้อยคุณต้องพิจารณาข้อเท็จจริงที่ว่าฐานข้อมูล SQL ไม่ได้มุ่งเน้นวัตถุ แต่ ORM มักจะมีเอนทิตีซึ่งเป็นวัตถุต่อตาราง ตั้งแต่เริ่มต้นคุณเสียการสู้รบไปแล้ว จากประสบการณ์ของฉันคุณไม่สามารถแสดงเอนทิตีในแบบที่คุณต้องการในแบบเชิงวัตถุ

ในฐานะที่เป็น " บริการ" บ๊อบมาร์ตินจะเป็นกับการมีระดับที่ชื่อว่า นั่นไม่ใช่วัตถุ บริการทำอะไร สิ่งใดที่เกี่ยวข้องกับการ มันอาจรวมทั้งจะมีป้ายกำกับ ฉันคิดว่าเขาต้องการสนับสนุนชั้นบริการ (ชื่อที่ดีกว่าจะเป็นชั้นตรรกะทางธุรกิจ) แต่ทุกชั้นในชั้นนั้นจะมีชื่อที่มีความหมาย FooBarServiceFooBarsFooBarUtils


2
เห็นด้วยกับจุดของคุณใน ORMs; พวกเขาเผยแพร่การโกหกที่คุณแมปเอนทิตีของคุณโดยตรงกับฐานข้อมูลกับพวกเขาเมื่อในความเป็นจริงนิติบุคคลอาจถูกจัดเก็บในหลายตาราง
Andy

@Daniel Kaplan คุณรู้หรือไม่ว่าลิงค์ที่อัพเดทสำหรับวิดีโอ Bob Martin คืออะไร?
Brian Morearty

25

ฉันกำลังทำงานเกี่ยวกับโครงการกรีนฟิลด์ในตอนนี้และเราต้องทำการตัดสินใจทางสถาปัตยกรรมเพียงเล็กน้อยเมื่อวานนี้ สนุกมากพอที่ฉันจะต้องทบทวนบางส่วนของ 'Patterns of Enterprise Application Architecture'

นี่คือสิ่งที่เราเกิดขึ้น:

  • ชั้นข้อมูล แบบสอบถามและฐานข้อมูลการอัพเดท เลเยอร์ถูกเปิดเผยผ่านที่เก็บแบบฉีดได้
  • เลเยอร์โดเมน นี่คือที่ตรรกะทางธุรกิจมีชีวิตอยู่ เลเยอร์นี้ใช้พื้นที่เก็บข้อมูลแบบฉีดและรับผิดชอบต่อตรรกะทางธุรกิจส่วนใหญ่ นี่คือแกนหลักของแอปพลิเคชันที่เราจะทดสอบอย่างละเอียด
  • ชั้นบริการ เลเยอร์นี้จะพูดถึงเลเยอร์โดเมนและทำหน้าที่ตามคำขอของลูกค้า ในกรณีของเราเลเยอร์บริการค่อนข้างง่าย - มันส่งต่อคำขอไปยังเลเยอร์โดเมนจัดการกับความปลอดภัยและข้อกังวลอื่น ๆ สิ่งนี้ไม่แตกต่างจากคอนโทรลเลอร์ในแอปพลิเคชั่น MVC มากนัก - คอนโทรลเลอร์มีขนาดเล็กและเรียบง่าย
  • ชั้นลูกค้า พูดคุยกับเลเยอร์บริการผ่าน SOAP

เราจบลงด้วยสิ่งต่อไปนี้:

ไคลเอ็นต์ -> บริการ -> โดเมน -> ข้อมูล

เราสามารถแทนที่ลูกค้าบริการหรือชั้นข้อมูลด้วยปริมาณงานที่เหมาะสม หากตรรกะโดเมนของคุณอยู่ในบริการและคุณตัดสินใจว่าคุณต้องการแทนที่หรือลบชั้นบริการของคุณคุณจะต้องย้ายตรรกะทางธุรกิจทั้งหมดไปที่อื่น ข้อกำหนดดังกล่าวหายาก แต่อาจเกิดขึ้นได้

ต้องบอกว่าทั้งหมดนี้ฉันคิดว่ามันค่อนข้างใกล้เคียงกับที่ Martin Fowler ตั้งใจพูด

บริการเหล่านี้อยู่บนสุดของโมเดลโดเมนและใช้โมเดลโดเมนสำหรับข้อมูล

แผนภาพด้านล่างแสดงให้เห็นว่าสิ่งนี้ค่อนข้างดี:

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

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


2
คุณตัดสินใจใช้สบู่เมื่อวานนี้เหรอ? นั่นเป็นข้อกำหนดหรือว่าคุณไม่มีความคิดที่ดีกว่า?
JensG

1
ส่วนที่เหลือจะไม่ตัดมันสำหรับความต้องการของเรา สบู่หรือส่วนที่เหลือนี่ไม่ได้สร้างความแตกต่างให้กับคำตอบ จากความเข้าใจของฉันบริการเป็นประตูสู่ตรรกะของโดเมน
CodeART

เห็นด้วยกับเกตเวย์อย่างแน่นอน SOAP คือ bloatware (มาตรฐาน) ดังนั้นฉันต้องถาม และใช่ไม่มีผลกระทบกับคำถาม / คำตอบอย่างใดอย่างหนึ่ง
JensG

6
ทำตัวเองชอบและฆ่าชั้นบริการของคุณ UI ของคุณควรใช้โดเมนของคุณโดยตรง ฉันเคยเห็นสิ่งนี้มาก่อนและโดเมนของคุณจะกลายเป็นกลุ่มของโรคโลหิตจางดีโตสไม่ใช่โมเดลที่สมบูรณ์
Andy

สไลด์เหล่านั้นครอบคลุมคำอธิบายเกี่ยวกับเลเยอร์บริการและกราฟิกด้านบนเรียบร้อยดี: slideshare.net/ShwetaGhate2/
......

9

นี่คือหนึ่งในสิ่งที่ขึ้นอยู่กับกรณีการใช้งาน จุดโดยรวมของชั้นบริการคือการรวมตรรกะทางธุรกิจเข้าด้วยกัน ซึ่งหมายความว่าผู้ควบคุมหลายคนสามารถเรียกใช้ UserService.MakeHimPay () เดียวกันได้โดยไม่ต้องสนใจว่าจะชำระเงินอย่างไร สิ่งที่เกิดขึ้นในบริการอาจง่ายพอ ๆ กับการแก้ไขคุณสมบัติของวัตถุหรืออาจเป็นการทำตรรกะที่ซับซ้อนเกี่ยวกับบริการอื่น ๆ (เช่นการโทรออกไปยังบริการของบุคคลที่สามเรียกตรรกะการตรวจสอบหรือแม้แต่เพียงบันทึกบางอย่างในฐานข้อมูล )

นี่ไม่ได้หมายความว่าคุณจะต้องตัดตรรกะทั้งหมดจากวัตถุโดเมน บางครั้งมันก็สมเหตุสมผลที่จะมีวิธีการในวัตถุโดเมนทำการคำนวณบางอย่างในตัวเอง ในตัวอย่างสุดท้ายของคุณบริการเป็นเลเยอร์ที่ซ้ำซ้อนเหนือวัตถุที่เก็บ / โดเมน มันให้บัฟเฟอร์ที่ดีกับการเปลี่ยนแปลงข้อกำหนด แต่มันไม่จำเป็นจริงๆ หากคุณคิดว่าคุณต้องการบริการลองใช้วิธี "แก้ไขคุณสมบัติ X บนออบเจ็กต์ Y" แบบธรรมดาแทนการใช้วัตถุโดเมน ตรรกะในคลาสโดเมนมีแนวโน้มที่จะตกอยู่ใน "คำนวณค่านี้จากฟิลด์" แทนที่จะเปิดเผยฟิลด์ทั้งหมดผ่าน getters / setters


2
ท่าทางของคุณในการสนับสนุนเลเยอร์บริการพร้อมตรรกะทางธุรกิจนั้นสมเหตุสมผล แต่ก็ยังมีคำถามอยู่บ้าง ในโพสต์ของฉันฉันได้อ้างอิงหลายแหล่งที่น่านับถือซึ่งพูดคุยเกี่ยวกับเลเยอร์บริการเป็นส่วนหน้าของโมฆะตรรกะทางธุรกิจใด ๆ ตรงนี้ตรงข้ามกับคำตอบของคุณคุณช่วยอธิบายความแตกต่างนี้ได้ไหม?
Jeroen Vannevel

5
ฉันพบว่ามันขึ้นอยู่กับประเภทของตรรกะทางธุรกิจและปัจจัยอื่น ๆ เช่นภาษาที่ใช้ ตรรกะทางธุรกิจบางอย่างไม่พอดีกับวัตถุโดเมนเป็นอย่างดี ตัวอย่างหนึ่งคือการกรอง / เรียงลำดับผลลัพธ์หลังจากดึงกลับจากฐานข้อมูล เป็นตรรกะทางธุรกิจ แต่ไม่สมเหตุสมผลกับวัตถุโดเมน ฉันพบว่าบริการที่ใช้ดีที่สุดสำหรับตรรกะอย่างง่ายหรือเปลี่ยนผลลัพธ์และตรรกะในโดเมนนั้นมีประโยชน์มากที่สุดเมื่อจัดการกับการบันทึกข้อมูลหรือการคำนวณข้อมูลจากวัตถุ
firelore

8

วิธีที่ง่ายที่สุดในการแสดงให้เห็นว่าทำไมโปรแกรมเมอร์จึงหลีกเลี่ยงการใส่ตรรกะโดเมนในวัตถุโดเมนคือพวกเขามักจะเผชิญกับสถานการณ์ที่ "ฉันจะใส่ตรรกะการตรวจสอบที่ไหน? นำวัตถุโดเมนนี้มาเป็นตัวอย่าง:

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;
        }
    }
}

ดังนั้นเราจึงมีตรรกะการตรวจสอบเบื้องต้นบางอย่างใน setter (ไม่สามารถลบได้) ปัญหาคือคุณไม่สามารถใช้ตรรกะนี้ซ้ำได้ ที่ใดที่หนึ่งมีหน้าจอหรือ ViewModel หรือตัวควบคุมที่ต้องทำการตรวจสอบก่อนที่จะยอมรับการเปลี่ยนแปลงในวัตถุโดเมนเพราะจะต้องแจ้งให้ผู้ใช้ทราบก่อนหรือเมื่อคลิกปุ่มบันทึกที่ไม่สามารถทำได้ และทำไม ทดสอบข้อยกเว้นเมื่อคุณเรียกใช้ตัวตั้งค่าว่าเป็นแฮ็คที่น่าเกลียดเพราะคุณควรทำการตรวจสอบความถูกต้องทั้งหมดก่อนที่คุณจะเริ่มทำธุรกรรม

MyEntityValidatorนั่นเป็นเหตุผลที่คนย้ายตรรกะการตรวจสอบชนิดของการบริการบางอย่างเช่น จากนั้นเอนทิตีและตรรกะการโทรจะได้รับการอ้างอิงไปยังบริการตรวจสอบความถูกต้องและนำกลับมาใช้ใหม่

หากคุณไม่ทำเช่นนั้นและคุณยังต้องการใช้ตรรกะการตรวจสอบซ้ำคุณจะต้องวางมันลงในวิธีการคงที่ของคลาสเอนทิตี:

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;
    }
}

สิ่งนี้จะทำให้รูปแบบโดเมนของคุณ "anemic" น้อยลงและใช้ตรรกะการตรวจสอบความถูกต้องที่อยู่ถัดจากพร็อพเพอร์ตี้ซึ่งดีมาก แต่ฉันไม่คิดว่าใครจะชอบวิธีการคงที่


1
ดังนั้นทางออกที่ดีที่สุดสำหรับการตรวจสอบในความคิดเห็นของคุณคืออะไร?
Flashrunner

3
@Flashrunner - ตรรกะการตรวจสอบของฉันมีความชัดเจนในชั้นตรรกะทางธุรกิจ แต่ยังซ้ำในชั้นนิติบุคคลและฐานข้อมูลในบางกรณี ชั้นธุรกิจจัดการอย่างดีโดยแจ้งผู้ใช้ ฯลฯ แต่ชั้นอื่น ๆ เพียงแค่โยนข้อผิดพลาด / ข้อยกเว้นและทำให้โปรแกรมเมอร์ (ตัวเอง) จากการทำผิดพลาดที่ข้อมูลเสียหาย
Scott Whitlock

6

ผมคิดว่าคำตอบเป็นที่ชัดเจนถ้าคุณอ่านโลหิตจางโดเมนรุ่นบทความของมาร์ตินฟาวเลอร์

การลบตรรกะทางธุรกิจซึ่งเป็นโดเมนออกจากโมเดลโดเมนคือการทำลายการออกแบบเชิงวัตถุ

มาทบทวนแนวคิดเกี่ยวกับวัตถุพื้นฐานที่สุด: วัตถุห่อหุ้มข้อมูลและการปฏิบัติงาน ตัวอย่างเช่นการปิดบัญชีเป็นการดำเนินการที่วัตถุบัญชีควรดำเนินการด้วยตนเอง ดังนั้นการให้ชั้นบริการดำเนินการนั้นไม่ใช่วิธีแก้ปัญหาเชิงวัตถุ เป็นขั้นตอนและเป็นสิ่งที่ Martin Fowler อ้างถึงเมื่อเขาพูดถึงโมเดลโดเมนโลหิตจาง

หากคุณมีเลเยอร์บริการปิดบัญชีแทนที่จะให้วัตถุบัญชีปิดตัวเองคุณจะไม่มีวัตถุบัญชีจริง บัญชี "วัตถุ" ของคุณเป็นเพียงโครงสร้างข้อมูล สิ่งที่คุณจะได้รับเมื่อมาร์ตินฟาวเลอร์แนะนำก็คือกระเป๋าที่มีผู้ได้รับและผู้ตั้งถิ่นฐาน


1
แก้ไข จริง ๆ แล้วฉันพบว่านี่เป็นคำอธิบายที่เป็นประโยชน์และไม่คิดว่ามันสมควรได้รับ downvotes
BadHorsie

1
มีอยู่ส่วนใหญ่ดูแลข้อเสียของแบบจำลองมากมาย และนั่นคือนักพัฒนาที่ดึงอะไรที่เกี่ยวข้องกับแบบจำลองเล็กน้อย สถานะของเปิด / ปิดเป็นแอตทริบิวต์ของบัญชีหรือไม่ แล้วเจ้าของล่ะ และธนาคารเหรอ พวกเขาควรจะถูกอ้างอิงโดยบัญชี? ฉันจะปิดบัญชีด้วยการคุยกับธนาคารหรือผ่านบัญชีโดยตรงหรือไม่ ด้วยโมเดลโรคโลหิตจางการเชื่อมต่อเหล่านั้นไม่ได้เป็นส่วนหนึ่งของโมเดล แต่จะถูกสร้างขึ้นเมื่อทำงานกับโมเดลเหล่านั้นในคลาสอื่น (เรียกว่าบริการหรือผู้จัดการ)
Hubert Grzeskowiak

4

คุณจะใช้ตรรกะทางธุรกิจของคุณในชั้นบริการอย่างไร เมื่อคุณชำระเงินจากผู้ใช้คุณเป็นการสร้างการชำระเงินไม่ใช่เพียงหักค่าจากคุณสมบัติ

วิธีการชำระเงินของคุณจำเป็นต้องสร้างบันทึกการชำระเงินเพิ่มหนี้ของผู้ใช้รายนั้นและยืนยันทั้งหมดนี้ในที่เก็บของคุณ การทำเช่นนี้ในวิธีการบริการนั้นตรงไปตรงมาอย่างไม่น่าเชื่อและคุณยังสามารถปิดการดำเนินการทั้งหมดในธุรกรรม การทำเช่นเดียวกันในรูปแบบโดเมนรวมเป็นปัญหามากกว่า


2

รุ่น tl; dr:
ประสบการณ์และความคิดเห็นของฉันบอกว่าวัตถุใด ๆ ที่มีตรรกะทางธุรกิจควรเป็นส่วนหนึ่งของแบบจำลองโดเมน แบบจำลองข้อมูลไม่น่าจะมีเหตุผลใด ๆ บริการน่าจะเชื่อมโยงทั้งสองเข้าด้วยกันและจัดการกับข้อกังวลข้าม (ฐานข้อมูลการบันทึก ฯลฯ ) อย่างไรก็ตามคำตอบที่ยอมรับได้เป็นคำตอบที่ใช้ได้จริงที่สุด

รุ่นที่ยาวกว่าซึ่งได้รับการพาดพิงถึงโดยผู้อื่นคือว่ามีคำที่ชัดเจนในคำว่า "แบบจำลอง" โพสต์จะสลับระหว่างตัวแบบข้อมูลและตัวแบบโดเมนราวกับว่าพวกมันเหมือนกันซึ่งเป็นข้อผิดพลาดทั่วไป นอกจากนี้ยังอาจมีการแยกออกเล็กน้อยในคำว่า "บริการ"

ในแง่ปฏิบัติคุณไม่ควรมีบริการที่เปลี่ยนแปลงวัตถุโดเมนใด ๆ เหตุผลนี้คือว่าบริการของคุณจะมีวิธีการบางอย่างสำหรับทุกคุณสมบัติในวัตถุของคุณเพื่อเปลี่ยนมูลค่าของทรัพย์สินนั้น นี่เป็นปัญหาเพราะถ้าคุณมีอินเทอร์เฟซสำหรับวัตถุของคุณ (หรือแม้ว่าจะไม่ใช่) บริการจะไม่ปฏิบัติตามหลักการเปิด - ปิดอีกต่อไป แต่เมื่อใดก็ตามที่คุณเพิ่มข้อมูลลงในโมเดลของคุณ (ไม่ว่าโดเมนกับข้อมูลใด) คุณจะต้องเพิ่มฟังก์ชั่นเพิ่มเติมลงในบริการของคุณ มีวิธีการบางอย่างอยู่รอบตัว แต่นี่เป็นสาเหตุที่พบบ่อยที่สุดที่ฉันเคยเห็นแอปพลิเคชัน "องค์กร" ล้มเหลวโดยเฉพาะอย่างยิ่งเมื่อองค์กรเหล่านั้นคิดว่า "องค์กร" หมายถึง "มีอินเทอร์เฟซสำหรับทุกวัตถุในระบบ" คุณลองนึกภาพการเพิ่มวิธีการใหม่ในอินเทอร์เฟซ จากนั้นไปยังการใช้งานที่ต่างกันสองหรือสามแบบ (ในแอปหนึ่ง, การใช้งานจำลอง, และดีบั๊กหนึ่ง, ในหน่วยความจำหนึ่ง?), เพียงแค่คุณสมบัติเดียวในแบบจำลองของคุณ? ฟังดูเหมือนเป็นความคิดที่แย่สำหรับฉัน

มีปัญหาอีกต่อไปที่นี่ที่ฉันจะไม่เข้าไป แต่สิ่งสำคัญคือ: การเขียนโปรแกรมเชิงวัตถุง่าย ๆ บอกว่าไม่มีใครนอกวัตถุที่เกี่ยวข้องควรจะสามารถเปลี่ยนค่าของคุณสมบัติภายในวัตถุหรือแม้แต่ " เห็น "มูลค่าของทรัพย์สินภายในวัตถุ สิ่งนี้สามารถบรรเทาได้ด้วยการทำให้ข้อมูลเป็นแบบอ่านอย่างเดียว คุณยังสามารถพบปัญหาเช่นเมื่อมีคนจำนวนมากใช้ข้อมูลแม้จะเป็นแบบอ่านอย่างเดียวและคุณต้องเปลี่ยนประเภทของข้อมูลนั้น เป็นไปได้ว่าผู้บริโภคทุกคนจะต้องเปลี่ยนเพื่อรองรับสิ่งนั้น นี่คือเหตุผลที่เมื่อคุณทำให้ทุกคนและทุกคนต้องใช้ API คุณจะได้รับคำแนะนำว่าไม่ควรเปิดเผยทรัพย์สินหรือข้อมูลสาธารณะ มันเป็นเหตุผลที่ OOP ถูกคิดค้นขึ้นในที่สุด

ฉันคิดว่าคำตอบส่วนใหญ่ที่นี่นอกเหนือจากคำตอบที่ได้รับการยอมรับนั้นกำลังทำให้เกิดปัญหา คนที่ทำเครื่องหมายว่ายอมรับนั้นดี แต่ฉันก็ยังรู้สึกว่าต้องตอบและเห็นด้วยว่า bullet 4 เป็นวิธีที่จะไปโดยทั่วไป

ใน DDD หมายถึงบริการเฉพาะสำหรับสถานการณ์เมื่อคุณมีการดำเนินการที่ไม่ได้เป็นของรากรวมใด ๆ คุณต้องระวังที่นี่เพราะบ่อยครั้งที่ความต้องการบริการสามารถบ่งบอกได้ว่าคุณไม่ได้ใช้รากที่ถูกต้อง แต่สมมติว่าคุณทำบริการจะใช้ในการประสานงานการดำเนินงานข้ามหลายรากหรือบางครั้งเพื่อจัดการข้อกังวลที่ไม่เกี่ยวข้องกับรูปแบบโดเมนเลย ...


1

คำตอบคือมันขึ้นอยู่กับกรณีการใช้งาน แต่ในสถานการณ์ทั่วไปส่วนใหญ่ฉันจะยึดมั่นกับตรรกะทางธุรกิจที่วางไว้ในชั้นบริการ ตัวอย่างที่คุณให้ไว้เป็นตัวอย่างที่ง่ายมาก อย่างไรก็ตามเมื่อคุณเริ่มนึกถึงระบบหรือบริการแยกจากกันและเพิ่มพฤติกรรมการทำธุรกรรมที่ด้านบนของมันคุณต้องการให้มันเกิดขึ้นเป็นส่วนหนึ่งของชั้นบริการ

แหล่งข้อมูลที่คุณยกมาสำหรับชั้นบริการที่ไร้ตรรกะทางธุรกิจใด ๆ แนะนำชั้นอื่นซึ่งเป็นชั้นธุรกิจ ในหลายสถานการณ์เลเยอร์บริการและชั้นธุรกิจจะถูกบีบอัดเป็นหนึ่งเดียว มันขึ้นอยู่กับว่าคุณต้องการออกแบบระบบของคุณอย่างไร คุณสามารถทำงานให้เสร็จสามชั้นและตกแต่งต่อไปและเพิ่มเสียงรบกวน

สิ่งที่คุณสามารถทำได้คือบริการแบบจำลองซึ่งรวมตรรกะทางธุรกิจเพื่อทำงานกับแบบจำลองโดเมนเพื่อคงสถานะไว้ คุณควรพยายามแยกบริการออกให้มากที่สุด


0

ในรูปแบบ MVC ถูกกำหนดให้เป็นตรรกะทางธุรกิจ การอ้างว่าควรจะอยู่ที่อื่นนั้นไม่ถูกต้องเว้นแต่เขาจะไม่ได้ใช้ MVC ฉันดูเลเยอร์บริการคล้ายกับระบบโมดูล ช่วยให้คุณสามารถรวมชุดของฟังก์ชั่นที่เกี่ยวข้องไว้ในแพ็คเกจที่ดี internals ของชั้นบริการนั้นจะมีรูปแบบการทำงานเช่นเดียวกับคุณ

โมเดลประกอบด้วยข้อมูลแอปพลิเคชันกฎธุรกิจตรรกะและฟังก์ชั่น http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller


0

แนวคิดของ Service layer สามารถดูได้จากมุมมอง DDD Aaronaught พูดถึงมันในคำตอบของเขาฉันแค่ทำอย่างละเอียด

วิธีการทั่วไปคือการมีคอนโทรลเลอร์ที่เฉพาะเจาะจงกับลูกค้าประเภทหนึ่ง บอกว่ามันอาจเป็นเว็บเบราว์เซอร์อาจเป็นแอปพลิเคชั่นอื่น ๆ ซึ่งอาจเป็นการทดสอบการทำงาน รูปแบบคำขอและการตอบกลับอาจแตกต่างกันไป ดังนั้นผมจึงใช้บริการแอพลิเคชันเป็นเครื่องมือสำหรับใช้เป็นสถาปัตยกรรมหกเหลี่ยม ฉันอัดฉีดคลาสโครงสร้างพื้นฐานเฉพาะคำขอที่เป็นรูปธรรม ตัวอย่างเช่นนั่นคือลักษณะที่คอนโทรลเลอร์ของฉันให้บริการการร้องขอเว็บเบราว์เซอร์:

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

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

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

หากฉันเขียนแบบทดสอบการใช้งานฉันต้องการใช้ไคลเอนต์การชำระเงินปลอมและบางทีฉันอาจไม่ต้องการการตอบกลับแบบ HTML ดังนั้นตัวควบคุมของฉันอาจมีลักษณะเช่นนั้น:

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

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

        return new JsonData($responseData);
    }
}

ดังนั้นบริการแอปพลิเคชันคือสภาพแวดล้อมที่ฉันตั้งค่าสำหรับการใช้งานตรรกะทางธุรกิจ นี่คือที่ซึ่งโมเดลคลาสถูกเรียกใช้ - การใช้โครงสร้างพื้นฐานแบบ agnostically

ดังนั้นตอบคำถามของคุณจากมุมมองนี้:

มันเป็นวิธีการเพียงดึงตรรกะจากตัวควบคุมและใส่ไว้ในบริการแทนหรือไม่

เลขที่

มันควรจะเป็นสัญญาระหว่างตัวควบคุมและโดเมนหรือไม่

อย่างใดอย่างหนึ่งสามารถเรียกได้ว่า

ควรมีเลเยอร์ระหว่างโดเมนกับเลเยอร์บริการหรือไม่

Nope


มีวิธีการที่แตกต่างกันอย่างสิ้นเชิง แต่ที่ปฏิเสธการใช้บริการใด ๆ ตัวอย่างเช่น David West ในหนังสือObject Thinkingอ้างว่าวัตถุใด ๆ ควรมีทรัพยากรที่จำเป็นทั้งหมดในการทำงาน วิธีการนี้จะส่งผลในการยกตัวอย่างเช่นการทิ้งออมใด


-2

สำหรับบันทึก.

SRP:

  1. Model = Data ไปที่ setter และ getters
  2. ลอจิก / บริการ = นี่เป็นการตัดสินใจ
  3. Repository / DAO = ที่นี่เราอนุญาตให้มีการจัดเก็บหรือดึงข้อมูล

ในกรณีนี้ตกลงเพื่อทำขั้นตอนต่อไป:

หากหนี้ไม่ต้องการการคำนวณ:

userObject.Debt = 9999;

อย่างไรก็ตามหากต้องมีการคำนวณให้ทำดังนี้

userObject.Debt= UserService.CalculateDebt(userObject)

หรือยัง

UserService.UpdateDebt(userObject)

แต่ถ้าการคำนวณเสร็จในเลเยอร์ persistence เช่นโพรซีเดอร์การจัดเก็บ

UserRepository.UpdateDebt(userObject)

ในกรณีนี้ฉันต้องการดึงผู้ใช้ออกจากฐานข้อมูลและอัปเดตหนี้เราควรทำหลายขั้นตอน (อันที่จริงแล้วสองข้อ) และไม่จำเป็นต้องห่อ / ใส่รหัสในฟังก์ชันของบริการ

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

และถ้ามันต้องการเก็บมันเราสามารถเพิ่มขั้นตอนที่สามได้

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

เกี่ยวกับแนวทางแก้ไขที่เสนอ

a) เราไม่ควรกลัวที่จะออกจากผู้พัฒนาเพื่อเขียนสองสามอย่างเพื่อห่อหุ้มมันในฟังก์ชั่น

b) และเกี่ยวกับอินเทอร์เฟซนักพัฒนาบางคนชอบอินเทอร์เฟซและก็ใช้ได้ แต่ในหลายกรณีพวกเขาไม่ต้องการเลย

c) เป้าหมายของบริการคือการสร้างโดยไม่มีคุณสมบัติส่วนใหญ่เป็นเพราะเราสามารถใช้ฟังก์ชั่นที่ใช้ร่วมกัน / คงที่ นอกจากนี้ยังง่ายต่อการทดสอบหน่วย


คำถามนี้ตอบคำถามได้อย่างไร
ริ้น

3
ประโยคชนิดใดที่เป็น"We shouldn't be afraid to left the end-developer to write a couple of instead of encapsulate it in a function."ฉันพูดได้แค่ Lewis Black" ถ้าไม่ใช่เพราะม้าของฉันฉันจะไม่ใช้เวลาในวิทยาลัยในปีนั้น "
Malachi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.