ViennaNET: một bộ thư viện dành cho phần phụ trợ. Phần 2

Cộng đồng nhà phát triển Raiffeisenbank .NET tiếp tục xem xét ngắn gọn nội dung của ViennaNET. Về cách thức và lý do chúng tôi đến đây, bạn có thể đọc phần đầu tiên.

Trong bài viết này, chúng ta sẽ điểm qua các thư viện chưa được xem xét để làm việc với các giao dịch, hàng đợi và cơ sở dữ liệu phân tán, có thể tìm thấy trong kho lưu trữ GitHub của chúng tôi (nguồn ở đây) và Gói Nuget ở đây.

ViennaNET: một bộ thư viện dành cho phần phụ trợ. Phần 2

ViennaNET.Sagas

Khi một dự án chuyển sang kiến ​​trúc DDD và microservice, khi logic nghiệp vụ được phân phối trên các dịch vụ khác nhau, một vấn đề sẽ phát sinh liên quan đến nhu cầu triển khai cơ chế giao dịch phân tán, vì nhiều kịch bản thường ảnh hưởng đến nhiều miền cùng một lúc. Bạn có thể làm quen với các cơ chế như vậy một cách chi tiết hơn, ví dụ: trong cuốn sách "Mô hình dịch vụ vi mô", Chris Richardson.

Trong các dự án của mình, chúng tôi đã triển khai một cơ chế đơn giản nhưng hữu ích: một câu chuyện, hay đúng hơn là một câu chuyện dựa trên sự phối hợp. Bản chất của nó như sau: có một kịch bản kinh doanh nhất định trong đó cần phải thực hiện tuần tự các hoạt động trong các dịch vụ khác nhau và nếu có bất kỳ vấn đề nào phát sinh ở bất kỳ bước nào, thì cần phải gọi quy trình khôi phục cho tất cả các bước trước đó, ở đâu. cung cấp. Do đó, khi kết thúc câu chuyện, bất kể thành công đến đâu, chúng tôi vẫn nhận được dữ liệu nhất quán trong tất cả các lĩnh vực.

Việc triển khai của chúng tôi vẫn được thực hiện ở dạng cơ bản và không bị ràng buộc với việc sử dụng bất kỳ phương pháp tương tác nào với các dịch vụ khác. Nó không khó sử dụng: chỉ cần tạo một hậu duệ của lớp trừu tượng cơ sở SagaBase<T>, trong đó T là lớp ngữ cảnh mà bạn có thể lưu trữ dữ liệu ban đầu cần thiết để saga hoạt động, cũng như một số kết quả trung gian. Phiên bản ngữ cảnh sẽ được chuyển qua tất cả các bước trong quá trình thực thi. Bản thân Saga là một lớp không trạng thái, do đó, phiên bản có thể được đặt trong DI dưới dạng Singleton để có được các phần phụ thuộc cần thiết.

Quảng cáo mẫu:

public class ExampleSaga : SagaBase<ExampleContext>
{
  public ExampleSaga()
  {
    Step("Step 1")
      .WithAction(c => ...)
      .WithCompensation(c => ...);
	
    AsyncStep("Step 2")
      .WithAction(async c => ...);
  }
}

Cuộc gọi ví dụ:

var saga = new ExampleSaga();
var context = new ExampleContext();
await saga.Execute(context);

Có thể xem ví dụ đầy đủ về các cách triển khai khác nhau đây và lắp ráp với kiểm tra.

ViennaNET.Orm.*

Một bộ thư viện để làm việc với nhiều cơ sở dữ liệu khác nhau thông qua Nhibernate. Chúng tôi sử dụng phương pháp tiếp cận DB-First bằng Liquibase, do đó chỉ có chức năng làm việc với dữ liệu trong cơ sở dữ liệu được tạo sẵn.

ViennaNET.Orm.Seedwork и ViennaNET.Orm – các tập hợp chính chứa các giao diện cơ bản và cách triển khai tương ứng. Hãy xem xét nội dung của họ chi tiết hơn.

Giao diện IEntityFactoryService và việc thực hiện nó EntityFactoryService là điểm khởi đầu chính để làm việc với cơ sở dữ liệu, vì Đơn vị công việc, các kho lưu trữ để làm việc với các thực thể cụ thể, cũng như trình thực thi lệnh và truy vấn SQL trực tiếp được tạo tại đây. Đôi khi, sẽ thuận tiện nếu hạn chế khả năng của một lớp khi làm việc với cơ sở dữ liệu, chẳng hạn như chỉ cung cấp khả năng đọc dữ liệu. Đối với những trường hợp như vậy IEntityFactoryService có một tổ tiên - giao diện IEntityRepositoryFactory, chỉ khai báo một phương thức tạo kho lưu trữ.

Để truy cập trực tiếp vào cơ sở dữ liệu, cơ chế nhà cung cấp được sử dụng. Mỗi DBMS chúng tôi sử dụng trong nhóm của mình đều có cách triển khai riêng: ViennaNET.Orm.MSSQL, ViennaNET.Orm.Oracle, ViennaNET.Orm.SQLite, ViennaNET.Orm.PostgreSql.

Đồng thời, một số nhà cung cấp có thể được đăng ký trong một ứng dụng cùng lúc, điều này cho phép, ví dụ, trong khuôn khổ một dịch vụ, mà không phải trả bất kỳ chi phí nào cho việc sửa đổi cơ sở hạ tầng, để thực hiện di chuyển từng bước từ DBMS này sang DBMS khác. Cơ chế chọn kết nối được yêu cầu và do đó, nhà cung cấp cho một lớp thực thể cụ thể (được viết ánh xạ tới các bảng cơ sở dữ liệu) được triển khai thông qua việc đăng ký thực thể trong lớp BoundedContext (chứa một phương thức đăng ký các thực thể miền) hoặc kế thừa của nó ApplicationContext (chứa các phương thức đăng ký thực thể ứng dụng, yêu cầu và lệnh trực tiếp), trong đó mã định danh kết nối từ cấu hình được chấp nhận làm đối số:

"db": [
  {
    "nick": "mssql_connection",
    "dbServerType": "MSSQL",
    "ConnectionString": "...",
    "useCallContext": true
  },
  {
    "nick": "oracle_connection",
    "dbServerType": "Oracle",
    "ConnectionString": "..."
  }
],

Ví dụ về bối cảnh ứng dụng:

internal sealed class DbContext : ApplicationContext
{
  public DbContext()
  {
    AddEntity<SomeEntity>("mssql_connection");
    AddEntity<MigratedSomeEntity>("oracle_connection");
    AddEntity<AnotherEntity>("oracle_connection");
  }
}

Nếu ID kết nối không được chỉ định thì kết nối có tên “mặc định” sẽ được sử dụng.

Ánh xạ trực tiếp các thực thể vào các bảng cơ sở dữ liệu được triển khai bằng các công cụ NHibernate tiêu chuẩn. Bạn có thể sử dụng mô tả cả thông qua tệp xml và thông qua các lớp. Để thuận tiện cho việc viết các kho lưu trữ sơ khai trong các bài kiểm tra Đơn vị, có một thư viện ViennaNET.TestUtils.Orm.

Có thể tìm thấy ví dụ đầy đủ về việc sử dụng ViennaNET.Orm.* đây.

ViennaNET.Messaging.*

Một tập hợp các thư viện để làm việc với hàng đợi.

Để làm việc với hàng đợi, cách tiếp cận tương tự đã được chọn như với các DBMS khác nhau, cụ thể là cách tiếp cận thống nhất tối đa có thể khi làm việc với thư viện, bất kể trình quản lý hàng đợi được sử dụng. Thư viện ViennaNET.Messaging chịu trách nhiệm chính xác cho sự thống nhất này, và ViennaNET.Messaging.MQSeriesQueue, ViennaNET.Messaging.RabbitMQQueue и ViennaNET.Messaging.KafkaQueue chứa các triển khai bộ điều hợp tương ứng cho IBM MQ, RabbitMQ và Kafka.

Khi làm việc với hàng đợi, có hai quy trình: nhận tin nhắn và gửi nó.

Hãy cân nhắc việc nhận. Ở đây có 2 lựa chọn: nghe liên tục và nhận một tin nhắn. Để liên tục lắng nghe hàng đợi, trước tiên bạn phải mô tả lớp bộ xử lý được kế thừa từ IMessageProcessor, sẽ chịu trách nhiệm xử lý tin nhắn đến. Tiếp theo, nó phải được “liên kết” với một hàng đợi cụ thể; việc này được thực hiện thông qua việc đăng ký trong IQueueReactorFactory chỉ ra mã định danh hàng đợi từ cấu hình:

"messaging": {
    "ApplicationName": "MyApplication"
},
"rabbitmq": {
    "queues": [
      {
        "id": "myQueue",
        "queuename": "lalala",
        ...
      }
    ]
},

Ví dụ về việc bắt đầu nghe:

_queueReactorFactory.Register<MyMessageProcessor>("myQueue");
var queueReactor = queueReactorFactory.CreateQueueReactor("myQueue");
queueReactor.StartProcessing();

Sau đó, khi dịch vụ khởi động và phương thức được gọi để bắt đầu nghe, tất cả các tin nhắn từ hàng đợi đã chỉ định sẽ được chuyển đến bộ xử lý tương ứng.

Để nhận được một tin nhắn trong giao diện gốc IMessagingComponentFactory có một phương pháp CreateMessageReceiversẽ tạo một người nhận đang chờ tin nhắn từ hàng đợi được chỉ định cho nó:

using (var receiver = _messagingComponentFactory.CreateMessageReceiver<TestMessage>("myQueue"))
{
    var message = receiver.Receive();
}

Gửi tin nhắn bạn cần phải sử dụng tương tự IMessagingComponentFactory và tạo một người gửi tin nhắn:

using (var sender = _messagingComponentFactory.CreateMessageSender<MyMessage>("myQueue"))
{
    sender.SendMessage(new MyMessage { Value = ...});
}

Có ba tùy chọn được tạo sẵn để tuần tự hóa và giải tuần tự hóa một tin nhắn: chỉ văn bản, XML và JSON, nhưng nếu cần, bạn có thể dễ dàng thực hiện triển khai giao diện của riêng mình IMessageSerializer и IMessageDeserializer.

Chúng tôi đã cố gắng duy trì các khả năng riêng biệt của từng người quản lý hàng đợi, ví dụ: ViennaNET.Messaging.MQSeriesQueue cho phép bạn gửi không chỉ văn bản mà còn cả tin nhắn byte và ViennaNET.Messaging.RabbitMQQueue hỗ trợ định tuyến và xếp hàng nhanh chóng. Trình bao bọc bộ điều hợp của chúng tôi cho RabbitMQ cũng triển khai một số giống RPC: chúng tôi gửi tin nhắn và chờ phản hồi từ hàng đợi tạm thời đặc biệt, hàng đợi này chỉ được tạo cho một tin nhắn phản hồi.

Đây một ví dụ về việc sử dụng hàng đợi với các sắc thái kết nối cơ bản.

ViennaNET.CallContext

Chúng tôi sử dụng hàng đợi không chỉ để tích hợp giữa các hệ thống khác nhau mà còn để liên lạc giữa các vi dịch vụ của cùng một ứng dụng, chẳng hạn như trong một câu chuyện. Điều này dẫn đến nhu cầu truyền cùng với thông báo các dữ liệu phụ trợ như thông tin đăng nhập của người dùng, số nhận dạng yêu cầu để ghi nhật ký từ đầu đến cuối, địa chỉ IP nguồn và dữ liệu ủy quyền. Để thực hiện việc chuyển tiếp dữ liệu này, chúng tôi đã phát triển một thư viện ViennaNET.CallContext, cho phép bạn lưu trữ dữ liệu từ một yêu cầu vào dịch vụ. Trong trường hợp này, yêu cầu được thực hiện như thế nào, thông qua hàng đợi hoặc qua Http, không thành vấn đề. Sau đó, trước khi gửi yêu cầu hoặc tin nhắn đi, dữ liệu sẽ được lấy từ ngữ cảnh và đặt vào tiêu đề. Do đó, dịch vụ tiếp theo sẽ nhận dữ liệu phụ trợ và quản lý dữ liệu đó theo cách tương tự.

Cảm ơn sự quan tâm của bạn, chúng tôi mong nhận được ý kiến ​​​​và yêu cầu kéo của bạn!

Nguồn: www.habr.com

Thêm một lời nhận xét