Any downsides to using the same table for multiple contexts?

Maybe not much simpler but at least a little :slight_smile:

  1. There should always be one context that is responsible for updating a given schema. That means that during a refactor addressgranular address the context that updates user would change the model too. So in this case Accounts.Model.User would change to those granular fields.

However, other contexts that only read and display user information would need only one change in the to_model. E.g. Billing context would only use that address as a whole and never change it.

With that in mind, you should never need the awkward from_model with splitting address by comma or translating changesets with different sets of fields.

  1. I am still figuring out how to fit changesets in the equation, but I understand this:

a) schema changeset should be hidden in MyApp.Accounts.IO.User
b) MyApp.Accounts.Model.User.new can return {:ok, %User{}} | {:error, changeset}. That changeset should have nothing to do with the database, only offline checks.

That model changeset in b) should be used for creating Phoenix form. I am still figuring out the details of this, but I came up with something like this:

defmodule MyApp.Accounts do
  alias MyApp.Accounts.Model.User
  alias MyApp.Accounts.IO

  def update_user(user_id, params) do
    with {:ok, old_user} <- IO.User.get(user_id), #returns Model.User not schema
         {:ok, new_user} <- User.new(Map.merge(User.to_map(old_user, params),
         {:ok, new_user} <- IO.User.update(old_user, new_user) do
       {:ok, new_user}
     else
       error -> error #the only type of error here is {:error, user_model_changeset}, even IO.User.update does not return schema changeset
  end
end

So changeset translation happens in IO and should be more straightforward because schemas should always have more granular fields.

  1. I am still figuring out what to do on new and edit actions that require the changeset. I am pretty sure I’d like to reuse Model.User changeset for building the form. Maybe something similar to change function from default Phoenix generators that returns the changeset.

One issue I had is that phoenix_ecto defines the implementation of Phoenix.Param protocol. Generated form automagically knows if it should use PUT for update or POST for create based on knowing if Changeset was loaded from the database or not.

If we have separate “model changeset” that never touches the database, we will need to pass the method by hand. It isn’t a big deal but another small annoyance when trying to fit this approach.