Reputation: 2285
Situation: We have some types of GQL that can provide icon names. Names are distinguished from different data properties (name, id, type, severity...). This is done by simple string transformations. At the end of the icons are some unified svg files somewhere in a CDN. The resources look like "Php", "Apache Server" etc. and the resolved icon names (keys) are "php", "apache-server" etc.
The question is what is better pattern
A. To add one resolver named "icon" for each type and provide all trnsformations on server-side. This means that GQL is more talkative, more data is transferred and FE code is more straightforward. The main arguments are that GQL should be client-oriented and the data are better to be consistent.
B. Or to provide the data as they are and transform them into icon names on FE between query results and rendering in an object-specific manner. This means having less redundancy on the API and more complexity in the FE code. The main argument for such an approach is that the icons are resources on the clinet side and are irrelevant to the server.
Please how to decide between A and B?
(the system is a business inteligence tool for low thousands simultaneous users)
Upvotes: 5
Views: 154
Reputation: 3095
It seems you are hitting a typical use case to implement a backend for frontend pattern where you perform on "server" some operations to satisfy "client" specific needs.
There are several factors to consider.
Data lifecycle
The map "icon", "type", "severity" seems to be relatively constant. Should the values change are you willing to do a new release or is it good for your use case to just act on the data level ?
Client, server and CDN are deployed in sync or your usecase allows different version between client and servers ?
In that cases, server side is the only option.
Performance
I'm working on BI and big data tools too :). Too many server side operations can be a mess but in that case i strongly suggest a load test. GraphQL can resolve with a very low footprint without relevant impact on server performance.
Separation of concerns
I have infered from your description that your application is not microservices based and has not a backend for frontend.
If you want to introduce on frontend calculation on backend, you have to keep it logically separated from the rest.
I strongly suggest you the modular monolith paradigm that allow you a good separation of concern even if you perform client side operations on server.
SVG Lifecycle
You said that SVG is on CDN, the resource is provided by you or is a 3rd party asset like fontawesome ?
This can be an issue if your team have different skills: an npm update
should not impact on server or db activities.
Please keep in mind how to maintain the SVG and icons map.
Complexity
Doing this mapping on frontend has not a big impact on client side performance and the complexity in very low and localized.
If you are on web client, ES6 features allows you a very readable mapping on client side with map and destructuring: the "complexity" is just on last 3 lines and is performed client side so no impact on your BI server.
const resources = {
php: {
name: "Php",
type: "application",
severity: "warn",
},
"apache-server": {
name: "Apache Server",
//
},
};
const genericResource = {
id: "generic",
name: "unknown resource",
type: "application",
severity: "warn",
};
const grapQLRequest = async () =>
Promise.resolve().then(() => [
{ resource: "php", message: "some string", timestamp: "" },
{ resource: "apache-server", message: "some string", timestamp: "" },
{ resource: "new-resource", message: "some string", timestamp: "" },
]);
const handler = async () => {
const result = await grapQLRequest().then((data) =>
data.map((x) => ({
...x,
resource: {
id: x.resource,
...(resources[x.resource] || genericResource),
},
}))
);
console.log(result);
};
void handler();
Keep resources nearby to its own usages
A very common code smell is shotgun surgery, with this approach you can bring your map very close to its own usages.
That can be more readable and maintainable, you don't need to think about separation of concerns.
I have a very low knowledge about your existing architecture and patterns, and i strongly suggest you to do a non-disruptive choice for your existing environment.
In your shoes I'll probably opt for Option B with some assumptions:
Bonus: If the list is small you can embed it on your own artifact, otherwise you can perform async import to import the resource only if is needed.
Upvotes: 2