Ash Framework: Coupled Change and Validation - Approaches?

I have a set of custom validations and changes that are tightly coupled together.

Imagine a create action:

...
validate check_something_about(:forms, extra_param)
change do_something_to(:forms, extra_param)
...

What is the “Ash” way of approaching this?

Should the validations and the changes be separated out (as in the example above) or should it all happen in the change, leveraging Ash.Changeset.add_error/3 to report errors?

Thanks.

Its an interesting question. What exactly makes them coupled? Are you expecting to reuse this combination in the future? There are a few ways to do it:

Call validations from the change

You could combine it by calling the validations in the change. If you have a reusable validation that you want wrapped into a bigger change.

with :ok <- Validation.validate(changeset, ...),
        :ok <- OtherValidation.validate(changeset, ...) do

end

Macro it

You could define a macro that includes both:

defmacro check_and_validate(key, param) do
  quote do
    check_something_aboute(:forms, extra_param)
    do_something_to(:forms, extra_param)
  end
end

And then in the action

create :create do
  change_and_validate(:forms, extra_param)
end

Share functionality with functions

If they are coupled in terms of functionality, then you could define a module with functions in it that allow them to share this functionality.

def change(changeset, _, _) do
  SharedBehavior.foo(changeset)
end

def validate(changeset, _) do
  SharedBehavior.foo(changeset)
end

Put it all in the change

At the end of the day, its perfectly acceptable to do a “validation” inside of a change, returning an error as you mentioned. Change modules are meant to be that outlet when their simpler counterpart, validations, don’t suffice.

2 Likes