บทความชุด .NET 6 แบบจับมือทำ — #9: Core Project

Ponggun
T. T. Software Solution
4 min readFeb 18, 2023

Core Project

ในส่วนของ Project นี้เราจะทำหน้าที่กำหนดในเรื่องของมาตราฐานในการพัฒนา Backing Service (เช่นการต่อ Database, Logging, Email) ผ่าน Interface (ต่อไปนี้จะขอเรียกว่า Infrastructure Interface) ส่วนการนำ Interface ไปพัฒนาต่อนั้นจะเป็นหน้าที่ของทาง Infra Project นะครับ

ในบทความนี้เราจะใช้ Repository Pattern เพื่อช่วยแยกความสัมพันธ์ระหว่าง Business Domain Model (อยู่ใน Domain Project) และ Data Access Layer (อยู่ใน Infra Project) ซึ่งจะทำให้เราทำ Mock Data เพื่อเขียน Unit Test ได้สะดวกขึ้นครับ

ทาง Microsoft ก็แนะนำให้ใช้ Repository Pattern เพื่อง่ายต่อการเขียน Unit Test นะครับ docs.microsoft.com/using-a-custom-repository-versus-using-ef-dbcontext-directly

Repository Pattern

ทำการกำหนดมาตราฐานของฟังชั่นที่ควรจะมีใน Base Repository Interface และ ทำการพัฒนาที่ Base Repository Class เพื่อที่ Repository อื่นจะได้นำไปใช้ต่อ

ต้องมี Repository Interface แยกตามแต่ละ Business Domain Model เพื่อรองรับ Query ที่แตกต่างกันไป เช่น บาง Repository อาจจะมีแค่การดึงข้อมูลง่ายๆจาก 1 Business Domain Model หรือบาง Repository อาจจะมีแค่การดึงข้อมูลจากหลายๆ Business Domain Model

Repository Interface อื่นๆสามารถลดเวลาการ Implement ตาม Base Repository Interfaceได้ด้วยการ สืบทอด Base Repository Class

พัฒนา Infrastructure Interface — Repository Pattern

อันดับแรกให้สร้าง Folder Interface/Infra/Database เพื่อเก็บ Interface ที่เกี่ยวข้องกับการสร้าง Repository นะครับ ส่วนการพัฒนา Interface เราจะไปทำที่ Infra Project กันครับผม

เสร็จแล้วให้สร้าง Files ดังนี้นะครับ

  • _IBaseRepository.cs => ใช้เพื่อเก็บโครงสร้างพื้นฐานที่ทุกๆ Repository ควรจะมีครับ
  • IProvinceRepository.cs => เน้นการจัดการข้อมูลที่เกี่ยวกับ Domain Model Province จาก Domain Project
  • IPointOfInterestRepository.cs => เน้นการจัดการข้อมูลที่เกี่ยวกับ Domain Model PointOfInterest จาก Domain Project

ถ้าเรามี Business Domain ใหม่ๆ เราก็สามารถเพิ่ม Repository Interface เข้ามาอีกได้นะครับ

ให้ทำการ Copy Code จากลิ้งข้างล่างนี้นะครับ

IBaseRepository จะรวบรวม Functions พื้นฐานที่ทุกๆ Repository ควรจะมีนะครับ
IProvinceRepository จะสืบทอด IBaseRepository เพื่อให้มีมาตราฐานเดียวกันแต่จะมีการระบุว่าใช้เพื่อจัดการข้อมูลที่เกี่ยวกับ Domain Model Province
IPointOfInterestRepository จะสืบทอด IBaseRepository เพื่อให้มีมาตราฐานเดียวกันแต่จะมีการระบุว่าใช้เพื่อจัดการข้อมูลที่เกี่ยวกับ Domain Model PointOfInterest

พัฒนา Infrastructure Interface — Email

อันดับแรกให้สร้าง Folder Interface/Infra/Email เพื่อเก็บ Interface ที่เกี่ยวข้องกับมาตราฐานการพัฒนา Email Interface กันนะครับ

เสร็จแล้วให้สร้าง File ชื่อ IEmailProvider.cs และ Copy Code จากที่นี้ได้เลยครับ

Email Interface ที่กำหนดมาตราฐานว่าต้องสามารถส่ง Email ในแบบ Async ได้

พัฒนา Business Service Interface-Token Service

บริการนี้จะทำหน้าที่ส่ง Token ไปทาง Email นะครับ

ให้เราสร้าง Folder ชื่อ Interfaces/Services และสร้าง File ชื่อ ITokenService.cs พร้อม Copy Code จากลิ้งข้างล่างนี้นะครับ

พัฒนา Business Service-Token Service

ถ้าเป็นการเขียน Code สมัยก่อน เราอาจจะคุ้นชินกับการที่อ้างอิงพวก Concrete Implementation เช่นระบุ Class ที่ Implement ทั้ง Configuration และ Code ที่ต่อไปยัง Email Provider พร้อมแล้วนะครับ

ในบทความนี้เราจะใช้ DI มาช่วยทำให้เป็นการอ้างอิงถึง Abstraction เช่น Interface แทนนะครับโดยจะทดลองนำ Infrastructure Interface — Email มาเรียกใช้ในบริการของการส่ง Token ไปทาง Email นะครับ ลุย !!!

ให้เพิ่ม Folder ชื่อ Services และสร้าง File ชื่อ TokenService.cs และ Copy Code ตามลิ้งนี้นะครับ

เราจะพบว่า เราได้ทำ DI โดยการนำ IEmailProvider มาเรียกใช้ใน Token Service นะครับ ซึ่งในจังหวะนี้เรายังไม่รู้ด้วยซ้ำว่า IEmailProvider จะถูกพัฒนายังไง มี Configuration อะไรบ้าง ต่อไป Email เจ้าไหนบ้าง ซึ่งตรงนี้ละเป็นเรื่องที่ดีมากๆที่ทำให้ Code มันยืดหยุ่น รองรับการเปลี่ยนได้ในอนาคตครับ เขียน Mock เพื่อทำ Unit Test ก็สะดวกด้วย

พัฒนา Business Service Interface— ProvinceService

ในขั้นตอนนี้ผมอยากสาธิตการเขียน CRUD ด้วยวิธีการคุยกันกับ Repository ผ่าน Interface แทนที่จะเป็นการคุยผ่าน Concrete Class — DAL ต่างๆแบบในอดีตนะครับ

เพื่อสื่อให้เห็นว่าเราสามารถแบ่งแยก Infra ต่างๆออกไปพัฒนาในภายหลังได้ ทำให้ระบบยืดหยุ่น สลับไปสลับมาระหว่าง Infra ที่จะนำมาใช้ได้ครับ เช่นอาจจะเคยต่อ MSSQL เราก็สามารถเปลี่ยนเป็น MySQL, PostgreSQL รวมไปถึง MongoDB ได้ในอนาคตครับ โดยไม่กระทบ Code ใน Core Project เลย เพราะ

ให้เราสร้าง File ชื่อ ProvinceService.cs และ Copy Code ตามลิ้งนี้นะครับ

ขออธิบายบางส่วนของ Code นะครับ

ในการ Create Province ขึ้นมาใหม่นั้น เราจะทำการตรวจสอบก่อนว่ามีชื่อที่ซ้ำในระบบหรือไม่ โดยการเรียกใช้ฟังชั่นจาก Base Repository ชื่อ DoesExists()

ซึ่งถ้าถามว่า ฟังชั่น DoesExists() ทำงานยังไง เอาจริงๆเราก็ยังไม่รู้เลยครับ แต่เราตั้งเป็นมาตราฐานไว้ใน Interface ว่าฟังชั่นนี้ให้ทำหน้าที่ตรวจสอบว่ามี Province ที่ถูกสร้างด้วย Name ที่ระบุหรือยังในฐานข้อมูลนะครับ ส่วนการพัฒนา Interface นี้ค่อยไปดูใน Infra Project ในหัวข้อถัดไปนะครับ (มันจะขัดๆกับคนที่เคยเขียนโปรแกรมสมัยก่อนที่เราไปสร้างการต่อ DB ให้เสร็จก่อนแล้วค่อยเอามาเรียกใช้ที่นี้นะครับ เราค่อยๆเรียนรู้กันไปนะครับ สู้ๆ)

หลังจากนั้นเราทำการแปลง Input Model มาเป็น Domain Model แล้วก็ส่งเข้าไปสร้างใน AddAsync() ต่อไปครับ

Key หลักของ Core Model คือการที่เรารับ Input มาและทำการแปลงเป็น Domain Model เพื่อ แก้ไขข้อมูลในระบบ เสร็จแล้วทำการแปลง Domain Model ให้หลายเป็น Output ตามแต่ละ Use Case ที่ API Project ต้องการนะครับ เราจะไม่ Expose Domain Model ออกไปที่ API Project ตรงๆเพื่อปกป้องข้อมูลในฐานข้อมูลของเราครับ

--

--

Ponggun
T. T. Software Solution

Development Manager, Web Developer with ASP.Net, ASP.net Core, Azure and Microsoft Technologies