Where should per-application validations go?

ecto
changesets
abstractions

#1

I have several backend applications that use their own changesets. In the frontend web application, I want to tie the interaction with these backends together to provide a better user experience. In other words, I want to merge changeset errors that the backend applications return and display those to the user.

The idea would be to use the changeset functions for each backend concern:

  def enroll(params) do
    enrollment = Portal.Enrollment.changeset(%Portal.Enrollment{}, params) # used for rendering the form

    user_changeset = Identity.User.changeset(%Identity.User{}, params)
    contact_changeset = Accounts.Contact.changeset(%Accounts.Contact{}, params)
    contact_point_changeset = Account.ContactPoint.changeset(%Accounts.ContactPoint, params)

    enrollment_changeset =
      Enum.reduce([user_changeset, contact_changeset, contact_point_changeset],
        enrollment,
        fn changeset, acc ->
          case Ecto.Changeset.apply_action(changeset, :insert) do
            {:ok, _data} ->
              acc

            {:error, changeset} ->
              %{acc | errors: acc.errors ++ changeset.errors}
          end
        end)

    ...
  end

%Portal.Enrollment{} is an embedded ecto schema used for rendering the forms on the web frontend.

What do you think of this approach? I guess one criticism would be that I should be more defensive and that these validations would be a better fit closer to the user. However, my concern is incongruity between the backend’s validations and the frontend’s. Do you a different way of managing this?