ชุมชนนักพัฒนา Raiffeisenbank .NET ยังคงตรวจสอบเนื้อหาของ ViennaNET โดยย่อต่อไป เกี่ยวกับวิธีการและเหตุผลที่เรามาถึงสิ่งนี้
ในบทความนี้ เราจะพูดถึงไลบรารีที่ยังไม่ได้รับการพิจารณาสำหรับการทำงานกับธุรกรรมแบบกระจาย คิว และฐานข้อมูล ซึ่งสามารถพบได้ในพื้นที่เก็บข้อมูล GitHub ของเรา (
ViennaNET.Sagas
เมื่อโปรเจ็กต์สลับไปใช้สถาปัตยกรรม DDD และไมโครเซอร์วิส เมื่อตรรกะทางธุรกิจถูกกระจายไปยังบริการต่างๆ ปัญหาก็เกิดขึ้นที่เกี่ยวข้องกับความจำเป็นในการใช้กลไกธุรกรรมแบบกระจาย เนื่องจากหลายสถานการณ์มักส่งผลกระทบต่อหลายโดเมนในคราวเดียว คุณสามารถทำความคุ้นเคยกับกลไกดังกล่าวได้อย่างละเอียดมากขึ้น เช่น
ในโครงการของเรา เราได้ใช้กลไกที่เรียบง่ายแต่มีประโยชน์: นิยายเกี่ยวกับวีรชน หรือนิยายเกี่ยวกับเรื่องราวที่เน้นการเรียบเรียง สาระสำคัญมีดังนี้: มีสถานการณ์ทางธุรกิจบางอย่างซึ่งจำเป็นต้องดำเนินการตามลำดับในบริการต่าง ๆ และหากเกิดปัญหาใด ๆ ในขั้นตอนใด ๆ จำเป็นต้องเรียกขั้นตอนการย้อนกลับสำหรับขั้นตอนก่อนหน้าทั้งหมดโดยที่ ที่ให้ไว้. ดังนั้นในตอนท้ายของตำนาน ไม่ว่าจะประสบความสำเร็จ เราก็ได้รับข้อมูลที่สอดคล้องกันในทุกโดเมน
การใช้งานของเรายังคงดำเนินการในรูปแบบพื้นฐานและไม่เกี่ยวข้องกับการใช้วิธีการโต้ตอบกับบริการอื่น ๆ การใช้งานไม่ยาก เพียงสร้างคลาสสืบทอดของคลาสนามธรรมพื้นฐาน SagaBase<T> โดยที่ T คือคลาสบริบทของคุณ ซึ่งคุณสามารถจัดเก็บข้อมูลเริ่มต้นที่จำเป็นสำหรับการทำงานของ Saga รวมถึงผลลัพธ์ระดับกลางบางส่วนได้ อินสแตนซ์บริบทจะถูกส่งผ่านไปยังทุกขั้นตอนระหว่างการดำเนินการ Saga นั้นเป็นคลาสไร้สัญชาติ ดังนั้นจึงสามารถวางอินสแตนซ์ใน DI เป็น Singleton เพื่อรับการขึ้นต่อกันที่จำเป็น
โฆษณาตัวอย่าง:
public class ExampleSaga : SagaBase<ExampleContext>
{
public ExampleSaga()
{
Step("Step 1")
.WithAction(c => ...)
.WithCompensation(c => ...);
AsyncStep("Step 2")
.WithAction(async c => ...);
}
}
ตัวอย่างการโทร:
var saga = new ExampleSaga();
var context = new ExampleContext();
await saga.Execute(context);
สามารถดูตัวอย่างการใช้งานต่างๆ แบบเต็มได้
ViennaNET.อรม.*
ชุดไลบรารีสำหรับการทำงานกับฐานข้อมูลต่างๆ ผ่าน Nhibernate เราใช้แนวทาง DB-First โดยใช้ Liquibase ดังนั้นจึงมีเพียงฟังก์ชันการทำงานสำหรับการทำงานกับข้อมูลในฐานข้อมูลสำเร็จรูปเท่านั้น
ViennaNET.Orm.Seedwork и ViennaNET.Orm
– แอสเซมบลีหลักที่มีอินเทอร์เฟซพื้นฐานและการใช้งานตามลำดับ เรามาดูรายละเอียดเนื้อหากันดีกว่า
อินเตอร์เฟซ IEntityFactoryService
และการนำไปปฏิบัติ EntityFactoryService
เป็นจุดเริ่มต้นหลักสำหรับการทำงานกับฐานข้อมูลเนื่องจากมีการสร้างหน่วยงานที่เก็บข้อมูลสำหรับการทำงานกับเอนทิตีเฉพาะตลอดจนผู้ดำเนินการคำสั่งและการสืบค้น SQL โดยตรง บางครั้งการจำกัดความสามารถของคลาสสำหรับการทำงานกับฐานข้อมูลก็สะดวก เช่น เพื่อให้ความสามารถในการอ่านข้อมูลเท่านั้น สำหรับกรณีดังกล่าว IEntityFactoryService
มีบรรพบุรุษ - อินเทอร์เฟซ IEntityRepositoryFactory
ซึ่งประกาศเฉพาะวิธีการสร้างที่เก็บข้อมูลเท่านั้น
ในการเข้าถึงฐานข้อมูลโดยตรง จะใช้กลไกของผู้ให้บริการ แต่ละ DBMS ที่เราใช้ในทีมของเรามีการใช้งานของตัวเอง: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql
.
ในเวลาเดียวกัน ผู้ให้บริการหลายรายสามารถลงทะเบียนในแอปพลิเคชันเดียวในเวลาเดียวกันได้ ซึ่งช่วยให้ดำเนินการย้ายข้อมูลแบบทีละขั้นตอนจากภายในกรอบของบริการเดียวได้โดยไม่มีค่าใช้จ่ายใดๆ สำหรับการปรับเปลี่ยนโครงสร้างพื้นฐาน DBMS หนึ่งไปยังอีกอันหนึ่ง กลไกในการเลือกการเชื่อมต่อที่ต้องการและดังนั้นผู้ให้บริการสำหรับคลาสเอนทิตีเฉพาะ (ซึ่งมีการเขียนการแมปกับตารางฐานข้อมูล) จะถูกนำไปใช้ผ่านการลงทะเบียนเอนทิตีในคลาส BoundedContext (ประกอบด้วยวิธีการสำหรับการลงทะเบียนเอนทิตีโดเมน) หรือตัวตายตัวแทน ApplicationContext (ประกอบด้วยวิธีการสำหรับการลงทะเบียนเอนทิตีแอปพลิเคชัน คำขอโดยตรง และคำสั่ง) โดยที่ตัวระบุการเชื่อมต่อจากการกำหนดค่าได้รับการยอมรับเป็นอาร์กิวเมนต์:
"db": [
{
"nick": "mssql_connection",
"dbServerType": "MSSQL",
"ConnectionString": "...",
"useCallContext": true
},
{
"nick": "oracle_connection",
"dbServerType": "Oracle",
"ConnectionString": "..."
}
],
ตัวอย่างบริบทของแอปพลิเคชัน:
internal sealed class DbContext : ApplicationContext
{
public DbContext()
{
AddEntity<SomeEntity>("mssql_connection");
AddEntity<MigratedSomeEntity>("oracle_connection");
AddEntity<AnotherEntity>("oracle_connection");
}
}
หากไม่ได้ระบุ ID การเชื่อมต่อ การเชื่อมต่อที่ชื่อ "ค่าเริ่มต้น" จะถูกนำมาใช้
การแมปโดยตรงของเอนทิตีกับตารางฐานข้อมูลถูกนำไปใช้โดยใช้เครื่องมือ NHibernate มาตรฐาน คุณสามารถใช้คำอธิบายทั้งผ่านไฟล์ xml และผ่านคลาส เพื่อความสะดวกในการเขียน Stub Repositories ใน Unit Tests มี Library ไว้ด้วย ViennaNET.TestUtils.Orm
.
สามารถดูตัวอย่างการใช้ ViennaNET.Orm.* ทั้งหมดได้
ViennaNET.ข้อความ.*
ชุดไลบรารีสำหรับการทำงานกับคิว
ในการทำงานกับคิว มีการเลือกวิธีการเดียวกันกับ DBMS ต่างๆ กล่าวคือ วิธีการแบบรวมที่เป็นไปได้สูงสุดในแง่ของการทำงานกับไลบรารี โดยไม่คำนึงถึงตัวจัดการคิวที่ใช้ ห้องสมุด ViennaNET.Messaging
มีหน้าที่รับผิดชอบในการรวมกลุ่มนี้อย่างชัดเจนและ ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue
มีการใช้งานอะแดปเตอร์สำหรับ IBM MQ, RabbitMQ และ Kafka ตามลำดับ
เมื่อทำงานกับคิว มีสองกระบวนการ: การรับข้อความและการส่ง
พิจารณารับ. มี 2 ตัวเลือกที่นี่: สำหรับการฟังอย่างต่อเนื่องและการรับข้อความเดียว หากต้องการฟังคิวอย่างต่อเนื่อง คุณต้องอธิบายคลาสตัวประมวลผลที่สืบทอดมาก่อน IMessageProcessor
ซึ่งจะรับผิดชอบในการประมวลผลข้อความที่เข้ามา ถัดไปจะต้อง "เชื่อมโยง" ไปยังคิวเฉพาะ ซึ่งทำได้โดยการลงทะเบียน IQueueReactorFactory
ระบุตัวระบุคิวจากการกำหนดค่า:
"messaging": {
"ApplicationName": "MyApplication"
},
"rabbitmq": {
"queues": [
{
"id": "myQueue",
"queuename": "lalala",
...
}
]
},
ตัวอย่างการเริ่มฟัง:
_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();
จากนั้นเมื่อบริการเริ่มต้นและเรียกใช้เมธอดเพื่อเริ่มฟัง ข้อความทั้งหมดจากคิวที่ระบุจะไปที่ตัวประมวลผลที่เกี่ยวข้อง
เพื่อรับข้อความเดียวในอินเทอร์เฟซของโรงงาน IMessagingComponentFactory
มีวิธีการ CreateMessageReceiver
ซึ่งจะสร้างผู้รับรอข้อความจากคิวที่ระบุ:
using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
var message = receiver.Receive();
}
เพื่อส่งข้อความ คุณต้องใช้แบบเดียวกัน IMessagingComponentFactory
และสร้างผู้ส่งข้อความ:
using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
sender.SendMessage(new MyMessage { Value = ...});
}
มีตัวเลือกสำเร็จรูปสามตัวเลือกสำหรับซีเรียลไลซ์และดีซีเรียลไลซ์ข้อความ: แค่ข้อความ, XML และ JSON แต่หากจำเป็น คุณสามารถสร้างการใช้งานอินเทอร์เฟซของคุณเองได้อย่างง่ายดาย IMessageSerializer и IMessageDeserializer
.
เราได้พยายามรักษาความสามารถเฉพาะตัวของผู้จัดการคิวแต่ละคน เช่น ViennaNET.Messaging.MQSeriesQueue
ช่วยให้คุณส่งไม่เพียงแต่ข้อความเท่านั้น แต่ยังรวมถึงข้อความแบบไบต์และ ViennaNET.Messaging.RabbitMQQueue
รองรับการกำหนดเส้นทางและการจัดคิวแบบทันที Wrapper อะแดปเตอร์ของเราสำหรับ RabbitMQ ยังใช้ RPC ที่มีลักษณะคล้ายกัน: เราส่งข้อความและรอการตอบกลับจากคิวชั่วคราวพิเศษ ซึ่งสร้างขึ้นสำหรับข้อความตอบกลับเดียวเท่านั้น
ที่นี่
ViennaNET.CallContext
เราใช้คิวไม่เพียงแต่สำหรับการบูรณาการระหว่างระบบที่แตกต่างกัน แต่ยังสำหรับการสื่อสารระหว่างไมโครเซอร์วิสของแอปพลิเคชันเดียวกัน เช่น ภายในเทพนิยาย สิ่งนี้นำไปสู่ความจำเป็นในการส่งข้อมูลเสริม เช่น การเข้าสู่ระบบของผู้ใช้ ตัวระบุคำขอสำหรับการบันทึกจากต้นทางถึงปลายทาง ที่อยู่ IP ต้นทาง และข้อมูลการอนุญาต เพื่อดำเนินการส่งต่อข้อมูลนี้ เราได้พัฒนาห้องสมุด ViennaNET.CallContext
ซึ่งช่วยให้คุณสามารถจัดเก็บข้อมูลจากการร้องขอที่เข้าสู่บริการได้ ในกรณีนี้ วิธีการส่งคำขอผ่านคิวหรือผ่าน Http นั้นไม่สำคัญ จากนั้น ก่อนที่จะส่งคำขอหรือข้อความขาออก ข้อมูลจะถูกดึงมาจากบริบทและวางไว้ในส่วนหัว ดังนั้นบริการถัดไปจะได้รับข้อมูลเสริมและจัดการในลักษณะเดียวกัน
ขอบคุณสำหรับความสนใจของคุณ เราหวังว่าจะได้รับความคิดเห็นของคุณและดึงคำขอ!
ที่มา: will.com