1 year ago

#338087

test-img

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

Accepted video resources