Looking for style advice when a function needs the `id` of an entity as a argument

I’m trying to be mindful of module coupling in my side project. I have a new module/context that is more built around a application behavior needs. To accomplish this behavior the app requires identifiers related to nouns defined in other contexts (User and Board).

In such a case do you think I should prefer the call site passing in the full User and Board entities or just ask for the ids directly?

Since I am using dialyzer I guess there is no real de-coupling here but curious which pattern people would prefer.

  @spec create_user_identity_prompt_event(User.id(), Board.id(), DateTime.t() | nil) ::
          {:ok, UserIdentityPromptEvent.t()} | {:error, Ecto.Changeset.t()}
  def create_user_identity_prompt_event(user_id, board_id, confirmed_at \\ nil) do
    # ...
  end

  @spec create_user_identity_prompt_event(User.t(), Board.t(), DateTime.t() | nil) ::
          {:ok, UserIdentityPromptEvent.t()} | {:error, Ecto.Changeset.t()}
  def create_user_identity_prompt_event(%User{id: user_id}, %Board{id: board_id}, confirmed_at \\ nil) do
    # ...
  end
1 Like

I am inclined to prefer the second style personally but I have found in our code-base sometimes we load a record from the database using its ID to pass it to such a function that throws away everything except the ID. So sometimes we add another clause that just takes the IDs, like your first example.

1 Like

I’m passing the IDs since in my case the authorization is part of the context function. So I have something like:

def update_message(%Account{} = account, id, attrs) do
  # get message by account.id and id
  # update
end

In my app, every user of an account has the same access rights. If I had different permissions per user, I would pass the user as the first argument instead.

If I were to pass the %Message{}, I’d have to put the authorization somewhere else. Keeping it in the context makes testing permissions easier.

1 Like

I think I’m going to keep the create_user_identity_prompt_event(user_id, board_id, confirmed_at \\ nil) function signature since the expected use of this function is a controller who will easily have access to both the user_id (via session) and board_id (via URL parameter) and if would feel cumbersome to require the controller to hydrate those into full entities for no real reason.

I do think, in isolation, it feels better to pass in the full User and Board as it just aligns with the terminology of the domain but feel like this is the right constraint for this use.

Thanks so much for sharing your observations in the thread.

3 Likes