Skip to content

Surface typed characteristics and pools in Instance JSON encoding #169

@matt-beanland

Description

@matt-beanland

Description

A resource that declares typed characteristics through the unified provider do characteristics do DSL — and pools via pools do — ends up with those records present in the graph as their own per-type resources (labels like :PathCharacteristic, :AssignableCharacteristic), wired to the Instance via :HAS. But the Instance's JSON encoding only picks up the dynamic Diffo.Provider.Characteristic records flowing through instance.characteristics, so the typed records and pool records are absent from serviceCharacteristic / resourceCharacteristic.

defmodule MyApp.Path do
  use Ash.Resource, fragments: [Diffo.Provider.BaseInstance], domain: MyApp.Access

  provider do
    specification do
      id "1d507914-…"
      type :resourceSpecification
      name "path"
    end

    characteristics do
      characteristic :path, MyApp.PathCharacteristic
    end
  end
end

After build, the PathCharacteristic record exists and exposes its own :value calculation; encoding the Instance still produces JSON without a resourceCharacteristic field.

What we need

That what was declared as a characteristic is also visible as one when the Instance is read — directly or through JSON encoding. The DSL declarations (characteristics/0, pools/0) are already exposed at compile time and the records are reachable in the graph; the gap is between "declared and stored" and "visible in the read view."

Why it matters

Typed characteristics and pools were introduced specifically so domain modellers could give characteristic values structure and lifecycle. If those records don't surface alongside the dynamic ones, every Extended domain has to either build the gather-and-merge view themselves or live with a partial round-trip. The risk for downstream consumers is a quiet one — defined values just don't appear — and the rediscovery cost happens once per domain.

There's a real question underneath this about where Diffo's responsibility ends and the consumer's begins (we ended up exploring both sides as we hit this), but landing the observation first feels more useful than leading with the abstract question.

A possible direction

The Util.suppress_rename(:characteristics, …) step is the natural place a wider input could land — characteristics/0 and pools/0 are already the inputs the DSL holds. Worth checking the symmetric story for features (featureCharacteristic).

A bigger-picture thought we drifted toward while exploring this: today Diffo's Provider domain effectively is the TMF638 projection — implicit, partial, and baked in. Making the projection explicit and opt-in (something like a tmf638 do DSL section a consumer declares when they want TMF638-compliant reads) would put the projection in the foreground, let Diffo do the gathering completely when asked, and leave Extended domains free to expose whatever shape they want when not. Equally, the right answer might just be a how-to in the usage rules pointing consumers at the building blocks for their own view. Both feel like valid evolutions — happy either way.

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