+1

OfX .NET, distributed data mapping.

Trong quá trình triển khai hệ thống Microservices(MC), chắc hẳn anh em ko ít lần mệt mỏi với việc mapping data trong hệ thống MC. Đối với dự án nho nhỏ và cấu trúc dữ liệu đơn giản thì có thể không mất nhiều công sức lắm nhưng khi dự án lớn hơn và dữ liệu trở nên phức tạp thì việc mapping data sẽ trở nên mệt mỏi và chán nản. Nay em xin giới thiệu với anh em 1 package .NET giúp việc mapping data trở nên đơn giản và funy hơn nhiều ạ. Ví dụ trước là em có 1 response trông như thế này ạ:

public class MemberResponse
{
    public string Id { get; set; }
    public string UserId { get; set; }
    public string UserName { get; set; }
    public string UserEmail { get; set; }
    public string UserProvinceId { get; set; }
    public string ProvinceName {get; set;}
    public string CountryName {get; set;}
}

Bối cảnh:

Member nằm ở service Workspace,

UserName, UserEmailUserProvinceId nằm ở service Identity,

ProvinceNameCountryName nẳm ở service MasterData.

Response trả về cho phía client là 1 array MemberResponse.

Hồi xưa thì ban đầu mình sẽ get response từ service Member ra được 1 array MemberResponse. Xong đó mình sẽ collect 1 list UserIds và lại gọi sang service Identity để lấy Name, Email, UserProvinceId. Xong đâu đó mình lại có 1 list UserProviceIds và lại tiếp tục gọi sang MasterData để lấy được thông tin ProvinceNameCountryName(đương nhiên CountryName thì không nằm cùng bảng với ProvinceName và có thể đó là phát sinh về business nên có khả năng mình phải coding thêm để lấy CountryName vào request của GetProvinces...) và rồi dữ liệu không chỉ như vậy, có thể phức tạp hơn rất nhiều... Hồi trước em gặp bài toán này nên đã tạo ra 1 thư viện để giải quyết bài toán distributed mapping gọi là OfX.

Vậy OfX giải quyết bài toán này như thế nào?

Trước tiên quay lại với response phía trên với 1 chút custom nho nhỏ:

public class MemberResponse
{
    public string Id { get; set; }
    public string UserId { get; set; }
    [UserOf(nameof(UserId))]
    public string UserName { get; set; }
    [UserOf(nameof(UserId), Expression = 'Email')]
    public string UserEmail { get; set; }
    [UserOf(nameof(UserId), Expression = 'ProvinceId')]
    public string UserProvinceId { get; set; }
    [ProvinceOf(nameof(UserProvinceId), Order = 1)]
    public string ProvinceName {get; set;}
    [ProvinceOf(nameof(UserProvinceId), Expression = "Country.Name", Order = 1)]
    public string CountryName {get; set;}
}

OfX sẽ đọc response của anh em xong đó graph lại những những Attribute nào có cùng kiểu như là UserOfAttribute, ProvinceOfAttribute... xong đó collect lại ids và gọi sang service tương ứng để lấy dữ liệu và mapping lại Properties tương ứng. Ngoài ra anh em sẽ thấy có thêm 1 cái là Order=1 ở đây là gì? Order = x nghĩa là các Properties được gắn với order đó sẽ mapping theo thứ tự, ví dụ ở trên: UserName, UserEmail, UserProvinceId sẽ được mapping trước. Xong đó ProvinceName và CountryName sẽ được mapping sau. Vì sao lại như vậy? Anh em thấy là mình cần có ProvinceId trước rồi mới có data để lấy dữ liệu của ProvinceNameCountryName đúng không ạ? OfX còn có thể mapping được cả Object, Array Object nữa. Về chi tiết thì mời anh em tham khảo qua repository: OfX. Cách cấu hình OfX cũng hết sức đơn giản. Anh em chỉ cần Install OfX core và Extension phù hợp với hệ thống anh em đang sử dụng. Hiện tại em đã support OfX.EntityFrameWorkCore với DataProvider và OfX.gRPC, OfX.Nats, OfX.RabbitMq và OfX.Kafka với Transports.
Dưới đây là cách cấu hình cơ bản với OfX, OfX-EFCore và OfX-Nats để anh em tham khảo (các Package khác cấu hình tương tự, em có ghi trong readMe của từng Package). Attributes

public sealed class UserOfAttribute(string propertyName) : OfXAttribute(propertyName);
public class ProvinceOfAttribute(string propertyName) : OfXAttribute(propertyName);

Note: Anh em nên tạo Attributes ở trong project Shared hoặc Common của anh em!

File Program.cs

builder.Services.AddOfX(cfg =>
{
    cfg.AddAttributesContainNamespaces(typeof(WhereTheAttributeDefined).Assembly);
    cfg.AddNats(config => config.Url("nats://localhost:4222"));
})
.AddOfXEFCore(options =>
{
    options.AddDbContexts(typeof(TestDbContext));
    options.AddModelConfigurationsFromNamespaceContaining<SomeModelAssemblyMarker>();
})
;

Models:

[OfXConfigFor<UserOfAttribute>(nameof(Id), nameof(Name))]
public class User
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public string ProvinceId {get; set;}
}
...
[OfXConfigFor<ProvinceOfAttribute>(nameof(Id), nameof(Name))]
public sealed class Province
{
    public ProvinceId Id { get; set; }
    public string Name { get; set; }
    public CountryId CountryId { get; set; }
    public Country Country { get; set; }
}

Vậy là xong rồi, anh em có thể tận hưởng kết quả rồi! Em có tạo 1 project demo và bên trong đó DTO response phức tạp hơn nhiều ạ. Mời anh em trải nghiệm: Repository: OfX. Link Demo: Demo.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí