1 year ago
#338087
mikeysee
Cross Cutting Concerns / Invariants / Domain Logic Query
Im finding this a tough question to phase accurately but here goes.
Suppose I have a request handler on my server that looks something like:
async function finishGame(userId: string, gameId: string) {
// grab a user
const user = await loadUser(userId);
// ... some misc logic
// Now we need to award the user some points for the finished game
user.points += 10
// We can now save the user
await saveUser(user);
}
Now lets say that we have another feature of our game where the user has medals awarded or removed depending on what their current points score is.
async function updateUserMedals(userId: string) {
// grab a user
const user = await loadUser(userId);
const medals = await loadUserMedals(userId);
// ... some misc logic
// Then finally save any changes
await saveUserMedals(updatedMedals);
}
The question is where should the logic live for changing a user's medals?
Should one inline it in the finishGame
method?
async function finishGame(userId: string, gameId: string) {
// grab a user
const user = await loadUser(userId);
// ... some misc logic
// Now we need to award the user some points for the finished game
user.points += 10;
// We can now save the user
await saveUser(user);
// Now we can update the user's medals
await updateUserMedals(user.id);
}
This feels a bit messy, there are multiple places in the game where a user's points can be changed so perhaps the points changing and the medals updating should live in the same function?
async function finishGame(userId: string, gameId: string) {
// grab a user
const user = await loadUser(userId);
// ... some misc logic
// Now we need to award the user some points for the finished game
await updateUserPoints(user, user.points + 10);
// We can now save the user
await saveUser(user);
}
async function updateUserPoints(userId: string, points: number) {
const user = await loadUser(userId);
user.points = points;
await updateUserMedals(user.id);
await saveUser(user);
}
Well now the logic is co-located in a single place which is good but now I am loading the user twice from the database which seems wasteful, not to mention that the calling function's user is now out of date.
Well perhaps then I should pass around the "User" object itself and not it's ID? Well the problem there is at what point do you "save" the user? Who's responsibility is it? Is it updateUserPoints
or is it finishGame
?
An alternative method to solve the problem would be to dispatch an event when the points are changed but again you have to be careful when to dispatch the event if I dispatch it from the updateUserPoints
but that method doesnt save the user then the event will be sent before the points have been updated so perhaps it should be sent from the end of finishGame
async function finishGame(userId: string, gameId: string) {
// grab a user
const user = await loadUser(userId);
// ... some misc logic
// Now we need to award the user some points for the finished game
user.points += 10;
// We can now save the user
await saveUser(user);
dispatchEvent("user-points-updated", { userId: user.id });
}
The issue is now we have separated out logic again which could be a problem.
Anyways I thankyou for reading down this far, if you have any suggestions to this problem im keen to hear.
node.js
typescript
asynchronous
architecture
software-design
0 Answers
Your Answer