# `Diffo.Provider.BaseInstance`
[🔗](https://github.com/diffo-dev/diffo/blob/v0.9.0/lib/diffo/provider/components/base_instance.ex#L5)

Ash Resource Fragment which is the shared base for your TMF Service or Resource Instance.

`BaseInstance` carries everything common to both kinds — identity, the graph
relationships (specification, characteristics, features, parties, places,
entities, notes, events, relationships), build wiring, the shared actions, and
the `Diffo.Provider.Instance.Extension` DSL. It is **not** composed alone:
pair it with a subtype fragment on a concrete leaf —

    use Ash.Resource, fragments: [Diffo.Provider.BaseInstance, Diffo.Provider.Service]   # a Service
    use Ash.Resource, fragments: [Diffo.Provider.BaseInstance, Diffo.Provider.Resource]  # a Resource

`Diffo.Provider.Service` (TMF638) adds the service lifecycle state machine
(`state` / `operating_status`) and lifecycle actions; `Diffo.Provider.Resource`
(TMF639) adds `lifecycle_state`. An instance is exactly one of Service or
Resource. `Diffo.Provider.Instance` is the generic Service + projection reader.

## Instance Extension DSL

All declarations live inside a single `provider do` section. It describes what the
instance kind is (`specification`, `characteristics`, `features`, `parties`, `places`)
and wires it to Ash actions (`behaviour`). The blocks below are each shown on their own
for clarity, but all sit inside one `provider do`.

`specification do` — declares the TMF Specification for this Instance kind (id, name, type,
major_version, description, category).

`characteristics do` — declares the top-level Characteristics of this Instance kind, each
backed by an `Ash.TypedStruct`.

`features do` — declares the Features this Instance kind may have, each optionally carrying
its own typed characteristic payload.

`parties do` — declares the Party roles this Instance kind relates to. Role names are
domain-specific nouns describing what the party means to the instance. Two forms:

    parties do
      party :provider, MyApp.Provider, calculate: :provider_calculation
      parties :installer, MyApp.Installer
      parties :technician, MyApp.Technician, constraints: [min: 1, max: 3]
      party_ref :owner, MyApp.InfrastructureCo
    end

- `party` — singular (at most one party in this role per instance)
- `parties` — plural (unbounded, or bounded with `constraints: [min: n, max: m]`)
- `party_ref` — a reference: no direct `PartyRef` edge; party is reachable by graph traversal
- `calculate:` — names an Ash calculation on this resource that produces the party at build time

`places do` — declares the Place roles this Instance kind relates to. Mirrors `parties do`:

    places do
      place :installation_site, MyApp.GeographicSite
      places :coverage_areas, MyApp.GeographicLocation, constraints: [min: 1]
      place_ref :billing_address, MyApp.GeographicAddress
    end

All declarations are introspectable at runtime via `Diffo.Provider.Instance.Info` and at
compile time via `Diffo.Provider.Instance.Extension.Info`.

`behaviour do actions do create :name end end` — marks a named create action for build
wiring. This injects `:specified_by`, `:features`, and `:characteristics` arguments onto
that action so Ash accepts the values that `build_before/1` sets automatically.

You still write the action body yourself for domain-specific accepts, arguments, and changes.
The build arguments are not public and do not need to appear in `accept`.

## Generated functions

Every resource using `BaseInstance` with a `specification do` gets the following functions
generated at compile time:

- `specification/0` — the specification keyword list baked at compile time
- `characteristics/0` — list of `Characteristic` structs
- `features/0` — list of `Feature` structs
- `parties/0` — list of `PartyDeclaration` structs
- `places/0` — list of `PlaceDeclaration` structs
- `characteristic/1` — returns the named `Characteristic` or `nil`
- `feature/1` — returns the named `Feature` or `nil`
- `feature_characteristic/2` — returns the named characteristic within a feature, or `nil`
- `party/1` — returns the `PartyDeclaration` for the given role, or `nil`
- `place/1` — returns the `PlaceDeclaration` for the given role, or `nil`
- `build_before/1` — called automatically before every create action; upserts the
  specification and creates features, characteristics, and parties, setting their ids
  as action arguments
- `build_after/2` — called automatically after every create action; relates the created
  TMF entities to the new instance node

Resources without a `specification do id` get trivial passthroughs for `build_before/1`
and `build_after/2`.

## Usage

    defmodule MyApp.Cluster do
      # a Cluster is a Resource, so it composes the Resource fragment
      use Ash.Resource, fragments: [BaseInstance, Diffo.Provider.Resource], domain: MyApp.Domain

      resource do
        description "A Cluster Resource Instance"
        plural_name :clusters
      end

      provider do
        specification do
          id "4bcfc4c9-e776-4878-a658-e8d81857bed7"
          name "cluster"
          type :resourceSpecification
        end

        parties do
          party :operator, MyApp.Organization
          parties :installer, MyApp.Engineer
        end

        places do
          place :site, MyApp.GeographicSite
        end

        behaviour do
          actions do
            create :build
          end
        end
      end

      actions do
        create :build do
          description "creates a new Cluster resource instance"
          accept [:id, :name, :type, :which]
          argument :relationships, {:array, :struct}
          argument :parties, {:array, :struct}

          change set_attribute(:type, :resource)
          change load [:href]
          upsert? false
        end
      end
    end

## Rolling your own actions

The `behaviour do actions do create :name end end` declaration is optional. Omitting it
means the `:specified_by`, `:features`, and `:characteristics` arguments are not declared
on that action — but `build_before/1` and `build_after/2` are still called for every
create via the global `BuildBefore` and `BuildAfter` changes registered on `BaseInstance`.

If you have a create action that should NOT trigger the full build wiring (e.g. a
lightweight admin create), you can override `build_before/1` or `build_after/2` on your
resource, or use Ash's `skip_unknown_inputs` to absorb the injected arguments without
declaring them.

## Instance versioning

Each Instance kind is tied to a specific major version of its Specification via the `id`
declared in `specification do`. Patch and minor version bumps update the existing
Specification node in place and require no instance changes. Major version bumps introduce
a new Instance kind module (e.g. `BroadbandV2`) with a new `id` and `major_version`,
leaving the original module and all its instances untouched.

To migrate an existing instance from one major version to another, call
`Diffo.Provider.respecify_instance/2` with the new specification's id:

    {:ok, v2_spec} = Diffo.Provider.get_specification_by_id(BroadbandV2.specification()[:id])
    {:ok, migrated} = Diffo.Provider.respecify_instance(instance, %{specified_by: v2_spec.id})

Any breaking data changes (e.g. a characteristic value that no longer exists in V2) must
be handled before or as part of respecification — either via Cypher directly against the
graph or via a domain-specific migration action you build on your own resource.

See `Diffo.Provider.Specification` for the full versioning lifecycle.

# `extensions`

# `opts`

# `persisted`

# `spark_dsl_config`

# `validate_sections`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
