ไปยังที่เก็บหรือไม่ไปยังที่เก็บ


10

เมื่อครั้งแรกที่ฉันได้เรียนรู้เกี่ยวกับการออกแบบการขับเคลื่อนด้วยโดเมนฉันยังได้รับการแนะนำให้รู้จักกับที่เก็บและรูปแบบการทำงานที่ครั้งหนึ่งดูเหมือนจะเป็นอันดับต้น ๆ สำหรับเด็กสุดเท่ห์ที่ทำให้แบบสอบถาม SQL เช่น cavemans เทียบกับฐานข้อมูล ยิ่งฉันเข้าสู่หัวข้อนั้นมากเท่าไหร่ฉันก็ยิ่งรู้ว่าพวกเขาไม่จำเป็นอีกต่อไปเพราะORMsเช่นEFและNHibernateที่ใช้ทั้งสองหน่วยงานและพื้นที่เก็บข้อมูลไว้ใน API เดียวที่เรียกว่าเซสชันหรือบริบท

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

  1. เลเยอร์โดเมนคือหัวใจของระบบที่มีเอนทิตี, บริการ, ที่เก็บ ...
  2. ชั้นโครงสร้างพื้นฐานให้การใช้งานของส่วนต่อประสานโดเมนที่เกี่ยวข้องกับโครงสร้างพื้นฐานเช่นไฟล์ฐานข้อมูลโปรโตคอล
  3. ชั้นแอพลิเคชันเป็นเจ้าภาพรากองค์ประกอบที่เชื่อมโยงสิ่งต่างๆและประสานทุกอย่าง

โซลูชันของฉันมักจะมีลักษณะเช่นนี้:

Domain.Module1
Domain.Module2
    IModule2Repo
    IModule2Service
    Module2
Infrastructure.Persistence
    Repositories
        EntityFrameworkRepositoryBase
MyApp
    Boostrapper
        -> inject EntityFrameworkRepositoryBase into IRepository etc.

ฉันรักษาเลเยอร์โดเมนของฉันให้สะอาดโดยใช้สิ่งIRepository<'T>ที่เป็นข้อกังวลเกี่ยวกับโดเมนไม่ขึ้นอยู่กับสิ่งอื่นใดที่บอกวิธีเข้าถึงข้อมูล เมื่อตอนนี้ฉันจะทำให้การใช้งานที่เป็นรูปธรรมของIModule2Serviceที่ต้องการเข้าถึงข้อมูลฉันจะต้องฉีดDbContextและโดยสิ่งนี้เชื่อมต่อโดยตรงกับชั้นโครงสร้างพื้นฐาน ( มาที่โครงการ Visual Studio สิ่งนี้อาจทำให้ยุ่งยากเพราะการอ้างอิงแบบวงกลม! )

นอกจากนี้สิ่งที่สามารถเป็นทางเลือกให้กับศูนย์รับฝากและงานfucktons ? CQRS? ใครจะสรุปกรอบโครงสร้างพื้นฐานที่บริสุทธิ์ได้อย่างไร


1
หากคุณกำลังพูดถึง 'พื้นที่เก็บข้อมูล' ตามที่อธิบายไว้ใน DDD แล้ว EF และ nHibernate ไม่ใช่ที่เก็บข้อมูล แน่นอนว่าพวกเขาบางส่วนที่เป็นนามธรรมกลไกของการเข้าถึงข้อมูล แต่มีมากขึ้นไปเป็นที่เก็บไปกว่านั้น
Eric King

คำตอบ:


4

วิศวกรรมคือทั้งหมดที่เกี่ยวกับการประนีประนอม และการพัฒนาซอฟต์แวร์ก็เช่นกัน ตอนนี้ฉันเชื่อว่ามีเพียงตัวเลือกอื่นที่ง่ายกว่าคือทำงานโดยตรงกับ ORM แต่อย่างที่คุณพูดนั่นอาจล็อคคุณเข้ากับกรอบการคงอยู่โดยเฉพาะ

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

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

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


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

เกี่ยวกับการเพิ่มวิธีใหม่ในการสืบค้นรูทรวมหรือเอนทิตีใด ๆ นั่นเป็นสาเหตุที่ CQRS ปรากฏ ส่วนตัวแล้วฉันเก็บ Repository ไว้เพื่อจุดประสงค์ในโดเมนและใช้ตัวจัดการคิวรี่สำหรับการสอบถามจริง และตัวจัดการเหล่านั้นเชื่อมต่ออย่างแน่นหนากับ db และ ofc อย่างมีประสิทธิภาพมากในสิ่งที่พวกเขาทำ
MikeSW

3

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

  • การดึงข้อมูล
  • การสร้างข้อมูล
  • การลบอย่างหนัก

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

การอัปเดตข้อมูลเป็นความรับผิดชอบของตรรกะทางธุรกิจที่ทำงานกับเอนทิตีที่มีปัญหา ซึ่งอาจรวมถึงสิ่งต่าง ๆ เช่นกฎการตรวจสอบ ฉันไม่ลอง encapsulating ในวิธีการเก็บข้อมูล

การลบในระบบส่วนใหญ่ของฉันคือการลบแบบอ่อนดังนั้นมันจะอยู่ภายใต้การปรับปรุงข้อมูล (IsActive = false) ในกรณีของการลบยากนี่จะเป็นหนึ่งซับในที่เก็บ

ทำไมต้องเป็นที่เก็บ ทดสอบความสามารถ แน่นอนว่า DbContext สามารถถูกเยาะเย้ยได้ แต่จะง่ายกว่าถ้าเยาะเย้ยคลาสที่คืนค่าIQueryable<TEntity>. พวกเขายังเล่นได้ดีกับรูปแบบ UoW โดยส่วนตัวแล้วฉันใช้รูปแบบ DbContextScope ของ Mehdime เพื่อกำหนดขอบเขตของหน่วยงานในระดับที่ฉันต้องการ (Ie Controllers ใน MVC) และให้ผู้ควบคุมและผู้ให้บริการชั้นเรียนใช้ที่เก็บข้อมูลโดยไม่ต้องผ่านการอ้างอิง UoW / dbContext รอบ ๆ การใช้ IQueryable หมายความว่าคุณไม่จำเป็นต้องใช้วิธีการห่อหุ้มจำนวนมากในที่เก็บและรหัสของคุณสามารถปรับวิธีการใช้ข้อมูลให้เหมาะสม ตัวอย่างเช่นพื้นที่เก็บข้อมูลไม่จำเป็นต้องเปิดเผยวิธีการเช่น "มีอยู่" หรือ "นับ" หรือพยายามห่อเอนทิตีกับ POCO อื่น ๆ ในกรณีที่คุณต้องการชุดข้อมูลย่อย พวกเขาไม่จำเป็นต้องจัดการกับตัวเลือกการโหลดกระตือรือร้นสำหรับข้อมูลที่เกี่ยวข้องที่คุณอาจหรืออาจไม่จำเป็น โดยผ่าน IQueryable รหัสการโทรสามารถ:

.Any()
.Count()
.Include() // Generally avoided, instead I use .Select()
.Where()
.Select(x => new ViewModel or Anon. Type)
.Skip().Take()
.FirstOrDefault() / .SingleOrDefault() / .ToList()

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

สำหรับที่เก็บทั่วไปฉันได้ย้ายออกไปจากที่นี่เป็นส่วนใหญ่เพราะเมื่อคุณจบลงด้วยที่เก็บต่อตารางตัวควบคุม / บริการของคุณจบลงด้วยการอ้างอิงไปยังที่เก็บหลายแห่งเพื่อดำเนินการทางธุรกิจเดียว ในกรณีส่วนใหญ่ที่เก็บข้อมูลเหล่านี้เพียงหนึ่งหรือสองแห่งกำลังดำเนินการเขียนจริง (หากคุณใช้คุณสมบัติการนำทางอย่างเหมาะสม) ในขณะที่ที่เหลือรองรับการอ่านด้วย ฉันอยากมีบางสิ่งบางอย่างเช่น OrdersRepository ที่สามารถอ่านและสร้างคำสั่งซื้อและอ่านการค้นหาที่เกี่ยวข้องอื่น ๆ (วัตถุลูกค้าที่มีน้ำหนักเบาผลิตภัณฑ์ ฯลฯ ) เพื่ออ้างอิงเมื่อสร้างคำสั่งซื้อแทนที่จะตี 5 หรือ 6 ที่เก็บที่แตกต่างกัน มันอาจละเมิด DNRY purists แต่ข้อโต้แย้งของฉันคือว่าวัตถุประสงค์ของที่เก็บคือเพื่อให้บริการการสร้างคำสั่งซื้อซึ่งรวมถึงการอ้างอิงที่เกี่ยวข้องRepository<Product>เพื่อให้ได้ผลิตภัณฑ์โดยใช้พื้นฐานของคำสั่งซื้อฉันจะต้องมีเอนทิตีที่มีฟิลด์จำนวนหนึ่งเท่านั้น OrderRepository ของฉันอาจมี.GetProducts()วิธีการส่งคืนIQueryable<ProductSummary>ซึ่งฉันพบว่าดีกว่าRepository<Product>ที่มีวิธี "รับ" หลายวิธีเพื่อลองและให้บริการพื้นที่ต่าง ๆ ตามความต้องการของแอปพลิเคชันและ / หรือนิพจน์การกรองแบบพาส - อินที่ซับซ้อน

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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.