0

This is a design problem that has plagued me from web apps to embedded systems. It seems that my fellow developers don't really care at all, so I've never seen an established pattern.

In short: how does one decide if the parameters of a function should take an ID related to an object or parts of/the entire object itself? Would you rather a function always look-up an object by ID - say from a DB or from a cache - or would you rather a function specify only the parts of the object it needs, and the caller must always fetch the object. Then - what about subsequent function calls?

Let's look at an example of purely ID based function parameters. I'll do this in JS and pretend it's a web API.

// Handle a request to get a user's profile
async function requestHandler(req, res) {
 if(req.url === 'users/profile'){
   const result = await getUserProfile(req.body.id);
   res(result);
 }
}

// Get the users profile from the db
async function getUserProfile(id){
  // Obviously, a db call will be done by ID in this case.
  // Let's assume it joins a few user tables together. 
  const data = await db.getUserData(id);

  // Now herein lies the question 
  // Suppose a slightly complex function that calculates stats
  // based off of a user's data. Should it accept an ID so that
  // it could be called elsewhere, or should it only accept the 
  // data it needs to calculate the stats?
  const stats = calculateUserStats(id | data);

  return {
    name: data.profile.name,
    stats,
  }
}

// In this version, the caller doesn't need to pre-retrieve all
// the necessary data, nor do they even need to know what it is
async function calculateUserStats(id){
  
}

// In this version, the caller must have retrieved the data already,
// potentially already retrieved naturally from another function..
function calculateUserStats(data){
  
}

In the above example, I try to illustrate that functions that may do more than one thing - like retrieve data, operate on the data through other functions, etc - could either be responsible for pre-retrieving that data or could just let the subsequent function fetch exactly what it needs on its own by only passing IDs. This example is brutally simple, but I find in all of the applications I work on, everyone has a mix of passing IDs and just passing the required data.

In some languages like C++ it's super simple because you can overload the function to support multiple parameters - but for languages like JS/TS, you can't do that. I suppose you could always write "functionXById" support functions.. maybe that is just the way to go?

1 Answer 1

2
  const data = await db.getUserData(id);

nit, review remark: Rather than a vague data, better to name it user.


how does one decide if the parameters of a function should take an ID related to an object or parts of/the entire object itself?

It's pretty simple. If you only have an ID, pass that around, and first function that does any work will have to retrieve the object. Once you have an object, keep passing that around, to ensure that someone down the call chain won't wastefully re-request it.

And if you're passing in a subset of the user fields, then it doesn't sound like you're passing them to a fooUser(first, last) function. It might be a capitalizeFullName function. It's not like knowing the class invariants of user helps you to reason about the function's behavior, so leave User out of the function name.


async function calculateUserStats(id){

If we're going for truth-in-advertising, a more honest function name might be fetchUserAndCalculateStats. Which doesn't sound to me like it has just a single responsibility.

Let's pretend the other one has a slightly different signature.

function calculateUserStats(user){

That looks perfect to me.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.