Skip to content

Provide change modules for the Define / Relate / Assign after-action patterns every Instance consumer writes #170

@matt-beanland

Description

@matt-beanland

Description

Every Diffo Instance consumer ends up writing the same after-action body inside its :define, :relate, and :assign_* update actions. The inputs are entirely Diffo-known — characteristics/0, pools/0, Characteristic.update_all/3, Pool.update_pools/3, Relationship.relate_instance/2, Assigner.assign/3 are all framework — yet each consumer re-writes the wiring on every action of every resource.

The three recurring shapes:

:define (~11 copies per typical consumer)

change after_action(fn changeset, result, _context ->
  with {:ok, result} <- Ash.load(result, [:characteristics]),
       {:ok, result} <- Characteristic.update_all(result, changeset, characteristics()),
       {:ok, result} <- Pool.update_pools(result, changeset, pools()),
       {:ok, result} <- MyDomain.get_<resource>_by_id(result.id),
       do: {:ok, result}
end)

:relate (~11 copies)

change after_action(fn changeset, result, _context ->
  with {:ok, result} <- Relationship.relate_instance(result, changeset),
       {:ok, result} <- MyDomain.get_<resource>_by_id(result.id),
       do: {:ok, result}
end)

:assign_ (~6 copies, parameterised by pool name)*

change after_action(fn changeset, result, _context ->
  with {:ok, result} <- Assigner.assign(result, changeset, :pairs),
       {:ok, result} <- MyDomain.get_<resource>_by_id(result.id),
       do: {:ok, result}
end)

In diffo_example we collapsed all three into local change modules:

update :define do
  argument :characteristic_value_updates, {:array, :term}
  change set_attribute(:resource_state, :operating)
  change DiffoExample.Changes.Define
end

update :relate do
  argument :relationships, {:array, :struct}
  change DiffoExample.Changes.Relate
end

update :assign_pair do
  argument :assignment, :struct, constraints: [instance_of: Assignment]
  change {DiffoExample.Changes.Assign, pool: :pairs}
end

That collapsed roughly 28 hand-written after-action bodies across 11 resources into three change modules — and read much closer to the intent expressed by the action name.

What we'd find useful

The three change modules (or whatever shape diffo prefers) shipped with diffo, so consumers can drop them in:

change Diffo.Provider.Changes.Define
change Diffo.Provider.Changes.Relate
change {Diffo.Provider.Changes.Assign, pool: :pairs}

The reload step can use the resource's primary :read action via Ash.Query.for_read(:read) (which is how our local versions ended up working) — so no consumer-specific reader is needed.

Why it matters

These three patterns are the connective tissue between Diffo's DSL declarations and what consumers actually do with them at runtime. Asking every consumer to thread characteristics(), pools(), Characteristic.update_all, Pool.update_pools, Relationship.relate_instance, Assigner.assign together in identical after-actions is making the consumer re-derive a contract Diffo already holds. It also drifts — the eleven copies across our domain were close to identical but not perfectly, and we caught a missing Ash.load(:characteristics) step that needed to be added everywhere when we hit a NotLoaded error.

A possible direction

Diffo.Provider.Changes.Define / Relate / Assign next to Diffo.Provider.Extension.Characteristic / Pool / Instance.Relationship feel like the natural home. The three local change modules we wrote are around 40-50 lines each and entirely Diffo-internal logic; we'd happily move them upstream if the maintainer agrees with the shape.

Related: #169 — the same observation pattern (Diffo holds the declarations, consumer re-derives them) shows up at JSON-encoding time too.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions