เอกสารนี้ให้รายละเอียดเกี่ยวกับ foodtruacker ซึ่งเป็นโครงการที่ใช้ Domain-Driven Design (DDD), CQRS และ Event Sourcing ใช้ ASP.NET Core และมุ่งเน้นไปที่การปรับปรุงการบำรุงรักษาในโดเมนธุรกิจที่ซับซ้อน โปรเจ็กต์นี้ใช้กรณีธุรกิจสมมติที่เรียบง่ายเพื่อวัตถุประสงค์ในการอธิบาย คำอธิบายโดยละเอียดนี้ครอบคลุมถึงแรงจูงใจ คุณลักษณะ รายละเอียดการใช้งาน และเทคโนโลยีที่เกี่ยวข้อง
foodtruacker - การดำเนินการ DDD, CQRS และการจัดหากิจกรรม
โครงการที่ขับเคลื่อนด้วยเหตุการณ์นี้ใช้หลักการ กรอบงาน และสถาปัตยกรรม ซึ่งทั้งหมดมีศูนย์กลางอยู่ที่แนวคิดในการปรับปรุงความสามารถในการบำรุงรักษาเมื่อต้องรับมือกับระบบที่สะท้อนถึงโดเมนธุรกิจที่ซับซ้อน Web API ของแอปพลิเคชันสร้างขึ้นบนเฟรมเวิร์ก ASP.NET Core ของ Microsoft และใช้การออกแบบที่ขับเคลื่อนด้วยโดเมน รวมถึง CQRS และรูปแบบการจัดหากิจกรรม กรณีทางธุรกิจสมมติเป็นรากฐานของโปรเจ็กต์นี้ และเป็นผลจากเวิร์กช็อปการบุกโจมตีเหตุการณ์
โปรดทราบ: โดเมนธุรกิจสมมติที่นำมาใช้กับโปรเจ็กต์นี้มีความเรียบง่ายอย่างมาก และควรถูกมองว่าเป็นผู้ให้บริการกรณีการใช้งานที่เกี่ยวข้องเท่านั้น
แรงจูงใจ
เนื่องจากการใช้การดำเนินการ CRUD และวัตถุ POCO ในโครงการที่มีโดเมนธุรกิจค่อนข้างซับซ้อนนั้นไม่ใช่วิธีที่ดีที่สุดเสมอไป ฉันจึงตัดสินใจสร้างโครงการนี้เพื่อเป็นการนำการวิจัยของฉันไปปฏิบัติจริงเกี่ยวกับ — และความสนใจใน — การออกแบบที่ขับเคลื่อนด้วยโดเมน (DDD) แนวทางการพัฒนาซอฟต์แวร์
เนื่องจากกรณีธุรกิจสมมติที่นำมาใช้กับโปรเจ็กต์นี้เน้นที่เหตุการณ์เป็นหลัก ฉันจึงตัดสินใจใช้รูปแบบ CQRS และ Event Sourcing ด้วย ทั้งสองดึงดูดความสนใจของฉันในขณะที่ทำการวิจัยสำหรับโครงการนี้และเข้ากันได้ดีกับ DDD
คุณสมบัติ
ภาพรวม
โปรเจ็กต์นี้ประกอบด้วยแอปพลิเคชัน Web API ที่สามารถเรียกใช้งานได้หนึ่งตัวและส่วนประกอบการทำงานหลายอย่าง โดยแต่ละแอปพลิเคชันมีให้ผ่านไลบรารีคลาส รหัสถูกจัดระเบียบตามเนมสเปซ ในแอปพลิเคชัน ASP.NET Core ที่สร้างใน Visual Studio ตามค่าเริ่มต้น เนมสเปซจะถูกสร้างขึ้นโดยอัตโนมัติจากโครงสร้างโฟลเดอร์ของโครงการ ดูแผนภาพด้านล่างสำหรับภาพรวมของโครงสร้างโฟลเดอร์ (และเนมสเปซ) ของโปรเจ็กต์นี้:
การแนะนำ
เหตุการณ์พายุ
รูปแบบเวิร์กช็อปที่ยืดหยุ่นสำหรับการสำรวจโดเมนธุรกิจที่ซับซ้อนร่วมกัน คิดค้นโดย Alberto Brandolini เป็นวิธีการที่ไม่ซับซ้อนมากสำหรับการปรับปรุง จินตนาการ สำรวจ และออกแบบกระแสและกระบวนการทางธุรกิจภายในองค์กรของคุณอย่างรวดเร็ว
เวิร์กช็อปประกอบด้วยกลุ่มบุคคลที่มีความเชี่ยวชาญที่แตกต่างกันโดยใช้กระดาษโน้ตสีเพื่อร่วมกันจัดวางกระบวนการทางธุรกิจที่เกี่ยวข้อง เวิร์กช็อป EventStorming จำเป็นต้องมีคนที่เหมาะสมและมีพื้นที่เพียงพอสำหรับติดกระดาษโน้ต โดยทั่วไปแล้วบุคคลที่ต้องการจะรวมถึงผู้ที่รู้คำถามที่จะถาม (โดยทั่วไปคือนักพัฒนา) และผู้ที่รู้คำตอบ (ผู้เชี่ยวชาญโดเมน เจ้าของผลิตภัณฑ์)
จุดมุ่งหมายของการประชุมเชิงปฏิบัติการนี้คือเพื่อให้ผู้เข้าร่วมเรียนรู้จากกันและกัน เปิดเผยและหักล้างความเข้าใจผิด และเช่น ในโครงการ GitHub นี้ จะเป็นรากฐานสำหรับการพัฒนาโซลูชันซอฟต์แวร์ตามเหตุการณ์ที่สะท้อนถึงโดเมนธุรกิจที่เชื่อมโยงกัน
การออกแบบที่ขับเคลื่อนด้วยโดเมน (DDD)
แนวทางการพัฒนาซอฟต์แวร์ที่เน้นการพัฒนาการเขียนโปรแกรมโมเดลโดเมนที่มีความเข้าใจอย่างลึกซึ้งเกี่ยวกับกระบวนการและกฎเกณฑ์ของโดเมนธุรกิจที่สัมพันธ์กัน คำว่า "การออกแบบที่ขับเคลื่อนด้วยโดเมน" ได้รับการประกาศเกียรติคุณจาก Eric Evans ในหนังสือชื่อเดียวกันของเขา
DDD มุ่งหวังที่จะลดความยุ่งยากในการสร้างแอปพลิเคชันที่ซับซ้อนและมุ่งเน้นไปที่หลักการหลักสามประการ:
หนังสือของ Eric Evans ให้คำจำกัดความทั่วไปบางประการสำหรับการออกแบบที่ขับเคลื่อนด้วยโดเมน:
โมเดลโดเมน
ระบบนามธรรมที่อธิบายกระบวนการและนโยบายของโดเมนธุรกิจ และใช้เพื่อจัดการงานที่จำเป็นที่เกี่ยวข้องกับโดเมนนั้น
ภาษาที่แพร่หลาย
คำและข้อความสำหรับองค์ประกอบบางอย่างของโดเมนธุรกิจ เพื่อป้องกันความเข้าใจผิดอีกครั้ง สมาชิกในทีมทุกคนควรใช้คำศัพท์บางคำ ซึ่งโดยทั่วไปจะใช้โดยผู้เชี่ยวชาญในโดเมน
บริบทที่ถูกผูกไว้
ขอบเขตแนวคิดที่มีการกำหนดและบังคับใช้โมเดลโดเมนเฉพาะ โดยทั่วไปจะแสดงถึงระบบย่อยหรือขอบเขตงาน ส่วนใหญ่เป็นการกำหนดขอบเขตทางภาษา โดยแต่ละบริบทที่มีขอบเขตจะมีภาษาที่แพร่หลายเป็นของตัวเอง
เช่น การจัดการลูกค้าที่ผู้ใช้เรียกว่า "ลูกค้า"
หนังสือของ Eric Evans ยังสร้างความแตกต่างให้กับบางส่วนของโมเดลโดเมนอีกด้วย เพื่อชื่อไม่กี่:
เอนทิตี
วัตถุที่กำหนดโดยเอกลักษณ์มากกว่าคุณลักษณะ
เช่น คนๆ หนึ่งจะยังคงเป็นคนคนเดิมเสมอ ไม่ว่าจะเลือกเสื้อแจ็คเก็ต สีผม หรือภาษาพูดในช่วงเวลาใดเวลาหนึ่งก็ตาม
วัตถุค่า
วัตถุที่ถูกกำหนดโดยค่าของแอตทริบิวต์เท่านั้น วัตถุค่าไม่เปลี่ยนรูปและไม่มีเอกลักษณ์เฉพาะ วัตถุค่าสามารถถูกแทนที่ด้วยวัตถุค่าอื่นที่มีคุณสมบัติเหมือนกัน
เช่น เมื่อเพ่งความสนใจไปที่บุคคล แว่นกันแดดที่ชำรุดสามารถเปลี่ยนเป็นแว่นกันแดดอันใหม่ที่ดูไม่แพ้กันได้อย่างง่ายดาย
รวม
คลัสเตอร์ของเอนทิตีตั้งแต่หนึ่งรายการขึ้นไปและออบเจ็กต์ค่าเสริม ซึ่งรวมเป็นหน่วยธุรกรรมเดียว เอนทิตีหนึ่งจะสร้างฐานของ Aggregate และด้วยเหตุนี้จึงมีการประกาศ Aggregate Root คุณสมบัติเอนทิตีที่ทำงานร่วมกันและออบเจ็กต์ค่าทั้งหมดสามารถเข้าถึงได้ผ่านเอนทิตีฐานเดียวนี้เท่านั้น ผลรวมจะต้องอยู่ในสถานะที่สอดคล้องกันเสมอ ในการเขียนโปรแกรมเชิงวัตถุ โดยทั่วไปจะทำโดยใช้ตัวตั้งค่าส่วนตัวและตัวรับที่ได้รับการป้องกัน
เช่น: ในบริบทการขายรถยนต์ รถยนต์หนึ่งคัน (เอนทิตี) ถูกกำหนดโดยหมายเลขประจำตัวยานพาหนะ รถคันนี้อาจมีสี่ล้อ (Value Objects) ซึ่งอาจจำเป็นต้องเปลี่ยนหลังจากช่วงระยะเวลาหนึ่ง
กิจกรรมโดเมน
ออบเจ็กต์ที่สร้างขึ้นจากผลลัพธ์ของกิจกรรมภายใน Domain Model ใช้เพื่อเก็บและส่งต่อข้อมูลที่เกี่ยวข้องกับกิจกรรมนี้ โดยทั่วไปกิจกรรมในโดเมนจะถูกสร้างขึ้นสำหรับกิจกรรมเหล่านั้นที่ผู้เชี่ยวชาญโดเมนพิจารณาว่าเกี่ยวข้อง
สถาปัตยกรรมหกเหลี่ยม (พอร์ตและอะแดปเตอร์)
รูปแบบสถาปัตยกรรมที่ใช้ในการออกแบบซอฟต์แวร์ เสนอโดย Alistair Cockburn ในปี 2548 รูปแบบนี้มีจุดมุ่งหมายเพื่อให้ได้การบำรุงรักษาในระดับสูง และอธิบายแอปพลิเคชันในสามชั้น แต่ละเลเยอร์สื่อสารกับเลเยอร์ที่อยู่ติดกันโดยใช้อินเทอร์เฟซ (พอร์ต) และการใช้งาน (อะแดปเตอร์):
กฎสำคัญในรูปแบบสถาปัตยกรรมนี้คือ การพึ่งพาสามารถชี้เข้าด้านในเท่านั้น ไม่มีสิ่งใดในวงในที่สามารถรู้สิ่งใดเกี่ยวกับบางสิ่งในวงนอกได้ การขึ้นต่อกันใดๆ ที่ต้องการชี้ให้เห็น เช่น การเรียกฐานข้อมูลจาก Application Layer จะต้องได้รับการสร้างอินสแตนซ์ผ่านการผกผันของ Control (IoC) หรือ Dependency Injection (DI)
CQRS ที่ใช้ MediatR (เฟรมเวิร์กการส่งข้อความที่สร้างไว้ล่วงหน้า)
CQRS ย่อมาจาก Command/Query Responsibility Segregation และได้รับการอธิบายครั้งแรกโดย Greg Young ในปี 2010 โดยอิงตามหลักการ Command Query Separation (CQS) และอนุญาตให้แยกการดำเนินการอ่านและเขียนได้ CQS ระบุว่า:
การปรับปรุงจาก CQRS บน CQS คือคำสั่งและการสืบค้นเหล่านั้นถือเป็นแบบจำลองแทนที่จะเป็นวิธีการ โมเดลเหล่านี้สามารถส่งเป็นอ็อบเจ็กต์ ณ จุดหนึ่ง จากนั้นได้รับการจัดการโดยตัวจัดการที่จำเป็นที่เกี่ยวข้อง ณ จุดอื่นในระบบ โดยแต่ละโมเดลจะส่งคืนโมเดลการตอบสนองเพื่อการแยกแต่ละการกระทำที่ชัดเจน
รูปแบบตัวกลางอนุญาตให้ใช้คำสั่ง/แบบสอบถามและตัวจัดการแบบหลวมๆ โดยใช้วัตถุตัวกลาง วัตถุไม่สื่อสารกันโดยตรงอีกต่อไป แต่สื่อสารผ่านตัวกลางแทน
กรอบงาน MediatR เป็นการนำรูปแบบสื่อกลางไปใช้แบบโอเพ่นซอร์ส ซึ่งสร้างโดย Jimmy Bogard มันจะถูกใช้ในโครงการนี้เพื่อการสื่อสารระหว่าง Framework Layer และ Application Layer นอกจากนี้ยังจะใช้สำหรับการฉายข้อมูลจากฐานข้อมูล Command ไปยังฐานข้อมูล Query
การจัดหากิจกรรม
รูปแบบการออกแบบทางสถาปัตยกรรมสำหรับจัดเก็บทุกการเปลี่ยนแปลงในสถานะของแอปพลิเคชัน แทนที่จะจัดเก็บเพียงสถานะปัจจุบันของข้อมูลในโดเมน รูปแบบนี้ได้รับการแนะนำโดย Greg Young และได้เห็นการนำไปใช้มากมายตั้งแต่นั้นมา
รูปแบบตั้งใจที่จะบันทึกทุกการเปลี่ยนแปลงสถานะของแอปพลิเคชันเป็นออบเจ็กต์เหตุการณ์ ออบเจ็กต์เหตุการณ์เหล่านี้จะถูกจัดเก็บตามลำดับของการเกิดขึ้นในลักษณะต่อท้ายเท่านั้น สิ่งนี้ไม่เพียงแต่ช่วยให้สามารถจำลองสถานะปัจจุบันของวัตถุตามลำดับเหตุการณ์ที่เกิดขึ้นจนถึงขณะนี้ได้ แต่ในท้ายที่สุดยังช่วยให้สามารถย้อนเวลากลับไปและสร้างสถานะของวัตถุขึ้นใหม่ในช่วงเวลาที่กำหนดได้
บัญชีธนาคารสามารถเป็นตัวอย่างที่ดีของหลักการจัดหากิจกรรมได้ ทุกครั้งที่ถอนหรือฝากเงิน แทนที่จะอัปเดตยอดคงเหลือปัจจุบัน จำนวนการเปลี่ยนแปลงจะถูกบันทึก ยอดคงเหลือปัจจุบันจะถูกคำนวณตามลำดับเหตุการณ์ โดยมีข้อมูลที่เกี่ยวข้องกับจำนวนเงินที่ถูกถอนหรือฝากในแต่ละครั้ง
การจัดหากิจกรรมทำงานได้ดีกับการออกแบบที่ขับเคลื่อนด้วยโดเมน เนื่องจากเหมาะอย่างยิ่งสำหรับการจัดเก็บกิจกรรมโดเมน ซึ่งทริกเกอร์โดยโมเดลโดเมนทุกครั้งที่มีการร้องขอการเปลี่ยนแปลง
การจัดหากิจกรรมยังได้รับประโยชน์อย่างมากจาก CQRS แทนที่จะต้องทำการสืบค้นกับฐานข้อมูล Event Sourcing ซึ่งจะต้องผ่านเหตุการณ์ที่บันทึกไว้ทั้งหมดที่เกี่ยวข้องกับออบเจ็กต์ที่ได้รับการร้องขอเพื่อสร้างสถานะปัจจุบันขึ้นมาใหม่ การสืบค้นนี้สามารถดำเนินการกับฐานข้อมูลการสืบค้นเฉพาะได้ ฐานข้อมูลแบบสอบถามนี้ได้รับการอัปเดตโดยตัวจัดการเหตุการณ์ของตนเอง โดยรับฟังเหตุการณ์เดียวกันที่ถูกส่งไปทันทีหลังจากถูกผนวกเข้ากับฐานข้อมูลการจัดหาเหตุการณ์ กระบวนการอัปเดตเหล่านี้เรียกว่าการฉายภาพ
การแยกฐานข้อมูลนี้ยังทำให้เกิดศักยภาพอย่างมากในการขยายขนาดและการเพิ่มประสิทธิภาพการทำงาน คุณสามารถสร้างอินสแตนซ์หลายอินสแตนซ์ของฐานข้อมูล Query และคงการซิงค์ไว้ได้ง่ายๆ โดยให้ตัวจัดการเหตุการณ์คอยฟังเหตุการณ์ที่ถูกส่งจากไคลเอ็นต์ฐานข้อมูลการจัดหากิจกรรมทันทีหลังจากการเปลี่ยนแปลงที่เกี่ยวข้องกับสถานะของแอปพลิเคชันเกิดขึ้น การเลือกประเภทฐานข้อมูลตลอดจนระดับของการทำให้ข้อมูลปกติดีขึ้น ซึ่งปรับให้เหมาะสมต่อการสืบค้น สามารถเพิ่มประสิทธิภาพได้อย่างมาก
การอัปเดตโมเดลการอ่านอย่างต่อเนื่องนี้สามารถเกิดขึ้นพร้อมกันหรืออะซิงโครนัสได้ อย่างหลังมาพร้อมกับต้นทุนของความสอดคล้องในที่สุด โดยโมเดลการอ่านไม่ซิงค์กับโมเดลการเขียนสำหรับช่องว่างเวลาเล็กน้อย (โดยปกติคือมิลลิวินาที)
เคอร์เนลที่ใช้ร่วมกัน
ไลบรารีทั่วไปสำหรับ Domain Layer ซึ่งประกอบด้วยคลาสพื้นฐานเฉพาะการออกแบบที่ขับเคลื่อนด้วยโดเมน เอนทิตีโดเมน วัตถุค่า ฯลฯ ซึ่งแชร์ข้ามบริบทที่มีขอบเขต
เริ่มต้นใช้งาน
หากต้องการให้โปรเจ็กต์นี้ทำงานได้ตามปกติ โปรดทำตามขั้นตอนเหล่านี้:
ข้อกำหนดเบื้องต้น
ตั้งค่า
เปิด https://localhost:5001/swagger/index.html ในเบราว์เซอร์ของคุณเพื่อดูเอกสาร Swagger ของ API ของคุณ
ใช้ Swagger, Postman หรือแอปพลิเคชันอื่นๆ เพื่อส่งคำขอ POST ไปที่ https://localhost:5001/api/Administration/Register เพื่อลงทะเบียนบัญชีผู้ดูแลระบบเริ่มต้นของคุณ ส่งวัตถุต่อไปนี้:
ตรวจสอบแอปพลิเคชันคอนโซลหรือเอาต์พุตใดก็ตามที่ได้รับการกำหนดค่าใหม่สำหรับบันทึกของแอปพลิเคชัน หลังจากการลงทะเบียนผู้ใช้สำเร็จแล้ว ควรมีลิงก์ยืนยันอีเมลที่จัดทำโดย EmailService เขียนลงในบันทึก คัดลอกและวาง URL นี้ลงในเบราว์เซอร์ของคุณแล้วกด Enter เพื่อลงทะเบียนให้เสร็จสิ้น รู้สึกอิสระที่จะเปลี่ยนแปลงหรือต่อยอดจากการใช้งานบริการอีเมลที่ไม่เหมาะสม ;-)
คุณพร้อมแล้ว เข้าสู่ระบบต่อไป
เปิด http://localhost:2113/ ในเบราว์เซอร์ของคุณเพื่อดู EventStoreDB GUI เปิดแท็บ "เบราว์เซอร์สตรีม" เพื่อดูกิจกรรมที่เก็บไว้ทั้งหมด
การทดสอบสามารถทำได้โดยการรัน:
เทคโนโลยี
โปรเจ็กต์นี้ใช้แพ็คเกจเทคโนโลยี / NuGet ต่อไปนี้:
แหล่งข้อมูล / การอ่านที่แนะนำ
อัลแบร์โต บรันโดลินี:
https://www.eventstorming.com
วอห์น เวอร์นอน:
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_1.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_2.pdf
https://dddcommunity.org/wp-content/uploads/files/pdfarticles/Vernon2011_3.pdf
อลิสแตร์ ค็อกเบิร์น:
https://web.archive.org/web/20180822100852/http://alistair.cockburn.us/Hexagonal+architecture
Robert C. Martin (ลุงบ๊อบ):
https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
ซีซาร์ เดอ ลา ตอร์เร, บิล วากเนอร์, ไมค์ รูซอส:
https://docs.microsoft.com/en-us/dotnet/architecture/microservices/
เกร็ก ยัง
https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf
https://cqrs.wordpress.com/documents/building-event-storage/
https://msdn.microsoft.com/en-us/library/jj591559.aspx
มาร์ติน ฟาวเลอร์:
https://www.martinfowler.com/bliki/CQRS.html
จิมมี่ โบการ์ด:
https://github.com/jbogard/MediatR
https://www.youtube.com/watch?v=SUiWfhAhgQw
การออกแบบที่ขับเคลื่อนด้วยโดเมน:
https://dddcommunity.org
https://thedomaindrivendesign.io
https://dotnetcodr.com/2013/09/12/a-model-net-web-service-based-on-domain-driven-design-part-1-introduction/
https://dotnetcodr.com/2015/10/22/domain-driven-design-with-web-api-extensions-part-1-notifications/
สถาปัตยกรรมหกเหลี่ยม:
https://fideloper.com/hexagonal-architecture
https://herbertograca.com/2017/09/14/ports-adapters-architecture/
เครดิต
http://www.andreavallotti.tech/en/2018/01/event-sourcing-and-cqrs-in-c/
https://www.Exceptionnotfound.net/real-world-cqrs-es-with-asp-net-and-redis-part-1-overview/
https://buildplease.com/pages/fpc-1/
https://dotnetcoretutorials.com/2019/04/30/the-mediator-pattern-in-net-core-part-1-whats-a-mediator/
https://itnext.io/why-and-how-i-implemented-cqrs-and-mediator-patterns-in-a-microservice-b07034592b6d