<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Quy Vu</title>
    <description>The latest articles on DEV Community by Quy Vu (@quyvu01).</description>
    <link>https://dev.to/quyvu01</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1340041%2F275146c6-b66e-4599-85a7-3138d14cf247.jpeg</url>
      <title>DEV Community: Quy Vu</title>
      <link>https://dev.to/quyvu01</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/quyvu01"/>
    <language>en</language>
    <item>
      <title>gRPC data mapping in .NET microservices: fill DTOs from other services without N+1 calls</title>
      <dc:creator>Quy Vu</dc:creator>
      <pubDate>Tue, 27 Oct 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/quyvu01/grpc-data-mapping-in-net-microservices-fill-dtos-from-other-services-without-n1-calls-363i</link>
      <guid>https://dev.to/quyvu01/grpc-data-mapping-in-net-microservices-fill-dtos-from-other-services-without-n1-calls-363i</guid>
      <description>&lt;p&gt;Your Order service stores a &lt;code&gt;UserId&lt;/code&gt;. The API that returns orders needs the user's name and email, and those live in the User service. So every team ends up writing the same glue: loop over the orders, call the User service, add a cache, add retries, and hope it doesn't become N+1 calls across the network.&lt;/p&gt;

&lt;p&gt;This post shows how to do that mapping over gRPC in .NET with &lt;a href="https://github.com/quyvu01/FxMap" rel="noopener noreferrer"&gt;FxMap&lt;/a&gt;, an open-source library (Apache-2.0) where you declare where a property comes from and one call fills it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvwhcw9jvd481ue0pwgk.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flvwhcw9jvd481ue0pwgk.gif" alt="FxMap filling UserName and UserEmail from the User service" width="759" height="356"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The usual way
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;_userClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetUserAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;GetUserRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserEmail&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A page of 50 orders is 50 gRPC calls. You fix it with a batch endpoint, then a cache, then you write the same thing again for &lt;code&gt;ProductName&lt;/code&gt;, &lt;code&gt;ProvinceName&lt;/code&gt;, and every other key in every other service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Declaring the mapping instead
&lt;/h2&gt;

&lt;p&gt;Both services share a key name. It is just a marker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserDistributedKey&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IDistributedKey&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The User service (owner of the data)&lt;/strong&gt; says how to find a user and what it can return:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserConfig&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EntityConfigureOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;User&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;DefaultProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;UseDistributedKey&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserDistributedKey&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddGrpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFxMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddEntitiesFromAssemblyContaining&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserConfig&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddEntityFrameworkCore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddDbContexts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UserDbContext&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Build&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapFxMapperGrpc&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The Order service (needs the data)&lt;/strong&gt; describes its DTO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderResponse&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserName&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;UserEmail&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderResponseProfile&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ProfileOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderResponse&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Configure&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;UseDistributedKey&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;UserDistributedKey&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;For&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="c1"&gt;// the default property (Name)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;For&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserEmail&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Services&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddFxMap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AddProfilesFromAssemblyContaining&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;OrderResponseProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddGrpcClients&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddGrpcHosts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://user-service:5001"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And in the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Orders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;OrderResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;UserId&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;ToListAsync&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;mapper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;MapDataAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What happens on the wire
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;FxMap collects the distinct &lt;code&gt;UserId&lt;/code&gt;s of all 50 orders and sends &lt;strong&gt;one&lt;/strong&gt; gRPC request to the User service.&lt;/li&gt;
&lt;li&gt;The User service answers from its &lt;code&gt;DbContext&lt;/code&gt; in one query and FxMap writes &lt;code&gt;UserName&lt;/code&gt; and &lt;code&gt;UserEmail&lt;/code&gt; back.&lt;/li&gt;
&lt;li&gt;Chains resolve level by level: &lt;code&gt;UserId → ProvinceId → CountryId&lt;/code&gt; is one request per level, not per object.&lt;/li&gt;
&lt;li&gt;The services share only a key name. No generated client per field, no shared DTO assembly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You are not limited to plain properties. The expression language reads navigations, filters and aggregates on the owner's side:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;For&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CountryName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Country.Name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;For&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DoneTotal&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Orders:where(Status = 'Done'):sum(Total)"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Not only gRPC, not only .NET
&lt;/h2&gt;

&lt;p&gt;gRPC is one of six transports. NATS, RabbitMQ, Kafka, Azure Service Bus and Amazon SQS use the same profiles, so switching transport doesn't touch your mapping code.&lt;/p&gt;

&lt;p&gt;The wire format is the same in &lt;a href="https://github.com/quyvu01/jfxmap" rel="noopener noreferrer"&gt;jfxmap&lt;/a&gt; (Java, Spring Boot) and &lt;a href="https://github.com/quyvu01/nfxmap" rel="noopener noreferrer"&gt;nfxmap&lt;/a&gt; (Node.js, TypeScript), so a .NET Order service can fill its DTO from a Java User service.&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it fast?
&lt;/h2&gt;

&lt;p&gt;The README has a BenchmarkDotNet comparison against AutoMapper &lt;code&gt;ProjectTo&lt;/code&gt; with the network removed: past a few dozen objects FxMap is on par or faster and allocates a half to a third of the memory, because each distinct key is fetched once. Numbers and the command to re-run them are &lt;a href="https://github.com/quyvu01/FxMap#performance" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package FxMap
dotnet add package FxMap.Grpc
dotnet add package FxMap.EntityFrameworkCore
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/quyvu01/FxMap" rel="noopener noreferrer"&gt;https://github.com/quyvu01/FxMap&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://fxmapper.net" rel="noopener noreferrer"&gt;https://fxmapper.net&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;gRPC transport: &lt;a href="https://github.com/quyvu01/FxMap/tree/main/src/FxMap.Grpc" rel="noopener noreferrer"&gt;https://github.com/quyvu01/FxMap/tree/main/src/FxMap.Grpc&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm the author and I'd like to hear how you solve this today. If you have a microservice setup where this would or wouldn't fit, tell me in the comments.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>microservices</category>
      <category>grpc</category>
    </item>
    <item>
      <title>Mapping distributed data across microservices, without the API spam or replication?</title>
      <dc:creator>Quy Vu</dc:creator>
      <pubDate>Mon, 05 Oct 2026 15:11:12 +0000</pubDate>
      <link>https://dev.to/quyvu01/mapping-distributed-data-across-microservices-without-the-api-spam-or-replication-129n</link>
      <guid>https://dev.to/quyvu01/mapping-distributed-data-across-microservices-without-the-api-spam-or-replication-129n</guid>
      <description>&lt;p&gt;Hi everyone,&lt;br&gt;
I recently ran into a problem with our microservices architecture: the data is spread across multiple services.&lt;br&gt;
For example, PatientId lives in Service A, but the actual patient info belongs to Service B. That's just a small example. I've got a ton of stuff to map, like master data, patient info, medicine info, and so on.&lt;br&gt;
I'd rather not write a bunch of API calls by hand or replicate data everywhere, so I've been working on a small library for it called FxMap. You declare what you need once with a fluent API, and it fetches and fills in the remote data over gRPC, RabbitMQ, Kafka, etc. It's still pretty young, so honest feedback is very welcome.&lt;br&gt;
Has anyone else dealt with this? Curious how you handled it, or if you'd do it differently.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>backend</category>
      <category>discuss</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Introduce a new way of distributed data mapping-OfX</title>
      <dc:creator>Quy Vu</dc:creator>
      <pubDate>Mon, 28 Apr 2025 15:38:02 +0000</pubDate>
      <link>https://dev.to/quyvu01/introduce-a-new-way-of-distributed-data-mapping-ofx-55fl</link>
      <guid>https://dev.to/quyvu01/introduce-a-new-way-of-distributed-data-mapping-ofx-55fl</guid>
      <description>&lt;p&gt;Hi everyone,&lt;br&gt;
I'm excited to introduce &lt;a href="https://ofx.world/" rel="noopener noreferrer"&gt;&lt;strong&gt;OfX&lt;/strong&gt;&lt;/a&gt; — an open-source library designed to &lt;strong&gt;simplify data mapping&lt;/strong&gt; across services, layers, and domains using an &lt;strong&gt;attribute-based&lt;/strong&gt; approach.&lt;/p&gt;

&lt;p&gt;OfX is a lightweight, high-performance, attribute-based data mapping engine designed for the .NET ecosystem. Built to empower developers to manage data transformations across services with clarity, speed, and scalability. Nowday, you just need to focus to your business rather than your code.&lt;br&gt;
Checkout here: &lt;a href="https://ofx.world/" rel="noopener noreferrer"&gt;https://ofx.world/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>programming</category>
      <category>opensource</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>.Net Attribute-based data mapping approach to streamline communication between services</title>
      <dc:creator>Quy Vu</dc:creator>
      <pubDate>Wed, 06 Nov 2024 06:41:31 +0000</pubDate>
      <link>https://dev.to/quyvu01/net-attribute-based-data-mapping-approach-to-streamline-communication-between-services-144f</link>
      <guid>https://dev.to/quyvu01/net-attribute-based-data-mapping-approach-to-streamline-communication-between-services-144f</guid>
      <description>&lt;p&gt;Hi everyone! I’m excited to share my latest project with you—a microservices-based solution incorporating modern patterns like CQRS, Event-Driven Architecture (EDA), and Saga orchestration. Additionally, I’ve implemented an attribute-based data mapping approach to streamline communication between services. My goal with this project is to help developers build faster, more stable, and scalable applications. I hope it proves valuable for anyone looking to enhance their microservices architecture! I’d love for you to check it out, and I welcome any contributions or feedback to make it even better. Let’s build something amazing together!&lt;/p&gt;

&lt;p&gt;Here is the project link:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/quyvu01/Anemoi_Open" rel="noopener noreferrer"&gt;https://github.com/quyvu01/Anemoi_Open&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we want to get UserData, just [UserOf]:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv47mmwqsl59hkcmuml5f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv47mmwqsl59hkcmuml5f.jpg" alt="Image description" width="800" height="443"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>csharp</category>
      <category>aspnet</category>
    </item>
  </channel>
</rss>
