Coding Style – Identifier vs Domain Object as Method Parameter

coding-stylemethodsparameters

Are there any objective arguments for or against using objects vs unique ID as method/function parameters? (and members of other objects?). Specially in the context of statically typed languages (C#/Java/Scala)

Pros of object itself:

  • More typesafe calls. With IDs there is a risk of wrong ordering of arguments. This can be mitigated though by keeping a 'mini' class for each class that only stores the ID of that class.
  • Get once from persistence, no need to get again
  • With ID, if the id type changes, say int -> long, then would require change across the board with possibility of mistakes.. (courtsey: https://softwareengineering.stackexchange.com/a/284734/145808)

Pros of using ID:

  • Most of the time no need for the actual object, just uniqueid would do, so having ID saves time from getting it from persistence.

A mixture of these techniques, as far as I can see, would only have cons of both and pros of neither.

Given that this is a concretely defined problem, I hope there are objective answers and not- "I think" or "I like" types… :-).

EDIT: Context on the suggestion of a commenter- The only restriction is a statically typed languages. The application is a general purpose application, although happy to get answers based on specific usage scenarios as well.

EDIT: Some more context. Say I have a book management system. My model is:

Book: { id: Int, isbn: String, donatedBy: Member, borrowedBy: Member }
Member: {id: Int, name: String}

Table- wishlist: { isbn: String, memberId: Int}
Table- borrows:  { id: Int, memberId: Int}  

Method: 
isInWishList( book: Book, member: Member ) vs isInWishList( bookId: Int,  memberId: Int)

handleBorrowRequest( memberId: Int, book: Book ){ 
   //the login system doesn't actually get the entire member structure, only the member id. I don't need the full member yet. can run a query to verify that the memberId has less than max allowed books for borrowing. 
}

Why would I want to keep only id and not the full member? Say when I get info about book, I rarely need to know about who has donated or borrowed it. Getting their full information to populate the donatedBy and borrowedBy fields is an overkill.

Ofcourse, these are only my points. I'm sure I'm missing things so wanted to know what are the points in favour/against.

Best Answer

Depending on your environment and problem context, the need to go the database can be mitigated, and as a result (IMO) the argument for using id's only is lost.

For example, PHP's Doctrine2 ORM has EntityManager::getReference which produces a lazily loaded proxy to an entity using the provided id; I don't know how many other ORM's do that, but it matches roughly the notion of a "mini-class" you mention. For most intents and purposes, this is a fully hydrated entity, so you can pass it around and use it like any other.

The only case in which it's not is when it's given an invalid id, and in that case you'd want to go to the database anyway to validate; plus, to reduce the cost of getting entities, most ORM's have (first & second level) caching functionality available in some form.

Once we reduce the need and cost of going to database, we're left largely with pro's of using the entity as a parameter:

  1. Type safety.
  2. Less knowledge required by the caller. A developer 6 months down the line can't mistakenly pass in the id of the wrong entity.
  3. Reduced refactoring cost. If a method were to change to require 2 pieces of information from the entity, there's no cost in changing the caller to provide it, presuming the caller got the fully hydrated entity to begin with.
  4. Less validation required per method. Many methods can assume that the entity they're given exists in the database (because it came from the ORM), and so no longer have to validate the id.
  5. (Potentially) fewer database calls. If you're passing id's to 3 methods, and each one has to validate it or fetch more data on the entity, that's 3 times as many database calls as 1 method fetching the entity and 2 operating on it.

Of course, there are cases where one might want to pass an id only: for example when crossing a bounded context boundary (in domain driven design speak). If we consider two bounded contexts with differing notions of what makes up an account (eg finance vs customer relations), then we can't pass context A's account to context B. Instead, an account id (in the language of the domain) can be passed across.