Repository navigation
FxMap vs Alternatives
Several ways exist to put data owned by other services into a response. Here is how they differ, what each costs, and when FxMap is the right fit.
In a microservice system a response often carries only keys: an order has a UserId and a ProductId. Showing it means collecting names and details from the services that own them, without a database join to lean on. Every approach below is an answer to that one problem.
Each endpoint calls the services that own the data and copies the values into the response.
Strengths
Fully explicit, no extra library, easy to debug.
Trade-offs
Repeated glue code in every endpoint; easy to introduce N+1 calls; every field is more code to maintain.
Choose it when
You have one or two enrichment points and a small team.
A gateway or backend-for-frontend calls several services and assembles the response.
Strengths
Keeps composition out of the domain services and close to the client.
Trade-offs
The composition logic still has to be written and kept in sync; the gateway becomes a place that knows every service.
Choose it when
Composition belongs to one client experience and one team owns the gateway.
A federated graph resolves fields across services through a gateway.
Strengths
Clients choose the fields they need; strong tooling and schema contracts.
Trade-offs
Requires adopting GraphQL and a gateway or router; resolvers and entity references to maintain.
Choose it when
You already expose GraphQL, or want clients to shape their own queries.
Services publish events and consumers keep a local copy of the data they need.
Strengths
Reads are local and fast; a consumer keeps working when the owner is down.
Trade-offs
Eventual consistency, storage duplication, and the work of building and operating the projections.
Choose it when
You need low-latency reads or high availability and can accept stale data.
In-process object-to-object mapping.
Strengths
Excellent at what they do: copying and shaping data you already have.
Trade-offs
They do not fetch data from other services, so they solve a different problem.
Choose it when
Always, for in-process mapping. They combine well with FxMap.
You declare which DTO properties come from which service; FxMap batches the ids, fetches the values over a transport and fills the DTO.
Strengths
Declarative, one request per key and level instead of one per object, compile-time analyzers, and it works across gRPC and message brokers. The owning service exposes entities through a configuration class rather than hand-written endpoints.
Trade-offs
Data is fetched at request time, so latency and availability depend on the owning service (retries and supervision help). Services share key names and need an entity config for each exposed entity.
Choose it when
Many endpoints return keys that need names and details from other services, and you want that as a declaration rather than glue code.
| Approach | Strengths | Trade-offs | Choose it when |
|---|
| Hand-written calls (HttpClient / gRPC clients)
Each endpoint calls the services that own the data and copies the values into the response.
| Fully explicit, no extra library, easy to debug. | Repeated glue code in every endpoint; easy to introduce N+1 calls; every field is more code to maintain. | You have one or two enrichment points and a small team. | |
API gateway or BFF composition
A gateway or backend-for-frontend calls several services and assembles the response.
| Keeps composition out of the domain services and close to the client. | The composition logic still has to be written and kept in sync; the gateway becomes a place that knows every service. | Composition belongs to one client experience and one team owns the gateway. | |
GraphQL federation
A federated graph resolves fields across services through a gateway.
| Clients choose the fields they need; strong tooling and schema contracts. | Requires adopting GraphQL and a gateway or router; resolvers and entity references to maintain. | You already expose GraphQL, or want clients to shape their own queries. | |
Read models (CQRS projections, event-carried state)
Services publish events and consumers keep a local copy of the data they need.
| Reads are local and fast; a consumer keeps working when the owner is down. | Eventual consistency, storage duplication, and the work of building and operating the projections. | You need low-latency reads or high availability and can accept stale data. | |
AutoMapper, Mapster, Mapperly
In-process object-to-object mapping.
| Excellent at what they do: copying and shaping data you already have. | They do not fetch data from other services, so they solve a different problem. | Always, for in-process mapping. They combine well with FxMap. | |
FxMap
You declare which DTO properties come from which service; FxMap batches the ids, fetches the values over a transport and fills the DTO.
| Declarative, one request per key and level instead of one per object, compile-time analyzers, and it works across gRPC and message brokers. The owning service exposes entities through a configuration class rather than hand-written endpoints. | Data is fetched at request time, so latency and availability depend on the owning service (retries and supervision help). Services share key names and need an entity config for each exposed entity. | Many endpoints return keys that need names and details from other services, and you want that as a declaration rather than glue code. |
- Many endpoints repeat the same lookups of names and details by key.
- You use .NET and a transport you already run (gRPC, NATS, RabbitMQ, Kafka, Azure Service Bus, Amazon SQS).
- You can accept request-time reads and want retries, supervision and tracing around them.
- All the data lives in one database: a join or a projection is simpler.
- You need reads to survive the owning service being down: use read models.
- Your clients already shape their own queries through GraphQL federation.
These approaches are not exclusive. FxMap also integrates with HotChocolate, so a GraphQL API can have its response types filled by FxMap.
Source: https://fxmapper.net/docs/compare/ — the website is always the most up to date version.
Introduction
Getting Started
Configuration
- Service Registration
- Entities & Profiles
- Handlers
- Pipelines
- Strongly Typed IDs
- Error Handling
- Retry Policy
- Supervisor
- Telemetry
- Kestrel (HTTP/2)
- Analyzers
Expressions
- Overview
- Property Access
- Filters, Sorting & Paging
- Functions
- Aggregations
- Projections
- Coalesce & Ternary
- GroupBy
Data Providers
Transports
Integrations
Quality
AI Assistants
Migration
Release Notes