You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
place :billing_address, MyApp.GeographicAddress, reference: true
352
+
end
353
+
354
+
```
355
+
356
+
357
+
358
+
359
+
### structure.places.place
360
+
```elixir
361
+
place role, place_type
362
+
```
363
+
364
+
365
+
Declares a singular place role on this Instance
366
+
367
+
368
+
369
+
370
+
371
+
### Arguments
372
+
373
+
| Name | Type | Default | Docs |
374
+
|------|------|---------|------|
375
+
|[`role`](#structure-places-place-role){: #structure-places-place-role .spark-required} |`atom`|| The role name, an atom |
376
+
|[`place_type`](#structure-places-place-place_type){: #structure-places-place-place_type } |`any`|| The module of the Place kind. A BasePlace-derived resource. |
377
+
### Options
378
+
379
+
| Name | Type | Default | Docs |
380
+
|------|------|---------|------|
381
+
|[`reference`](#structure-places-place-reference){: #structure-places-place-reference } |`boolean`|`false`| If true, no direct PlaceRef edge is created; the place is reachable by graph traversal. |
382
+
|[`calculate`](#structure-places-place-calculate){: #structure-places-place-calculate } |`atom`|| Name of an Ash calculation on this resource that produces the place at build time. |
|[`role`](#structure-places-places-role){: #structure-places-places-role .spark-required} |`atom`|| The role name, an atom |
409
+
|[`place_type`](#structure-places-places-place_type){: #structure-places-places-place_type } |`any`|| The module of the Place kind. A BasePlace-derived resource. |
410
+
### Options
411
+
412
+
| Name | Type | Default | Docs |
413
+
|------|------|---------|------|
414
+
|[`reference`](#structure-places-places-reference){: #structure-places-places-reference } |`boolean`|`false`| If true, no direct PlaceRef edge is created; the place is reachable by graph traversal. |
415
+
|[`calculate`](#structure-places-places-calculate){: #structure-places-places-calculate } |`atom`|| Name of an Ash calculation on this resource that produces the place at build time. |
416
+
|[`constraints`](#structure-places-places-constraints){: #structure-places-places-constraints } |`keyword`|| Multiplicity constraints on the number of places in this role, e.g. [min: 1, max: 3]|
Copy file name to clipboardExpand all lines: documentation/how_to/use_diffo_provider_extension.livemd
+78-22Lines changed: 78 additions & 22 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -35,6 +35,7 @@ In this 'Diffo Provider Instance Extension' livebook you will learn about:
35
35
* Using the Assigner
36
36
* Composing a Resource from partially assigned Resources
37
37
* Declaring domain Parties using the Party Extension
38
+
* Declaring domain Places using the Place Extension
38
39
39
40
### Installing Neo4j and Configuring Bolty
40
41
@@ -146,7 +147,7 @@ Diffo also has an inbuilt Spark DSL extension [Diffo.Provider.Instance.Extension
146
147
147
148
The extension has two top-level sections:
148
149
149
-
**`structure do`** — describes the static shape of the Instance kind: its TMF Specification, Characteristics, Features, and Party roles. All declarations are baked into the module at compile time and introspectable at runtime via generated functions (`specification/0`, `characteristics/0`, `features/0`, `parties/0`) and `Diffo.Provider.Instance.Info`.
150
+
**`structure do`** — describes the static shape of the Instance kind: its TMF Specification, Characteristics, Features, Party roles, and Place roles. All declarations are baked into the module at compile time and introspectable at runtime via generated functions (`specification/0`, `characteristics/0`, `features/0`, `parties/0`, `places/0`) and `Diffo.Provider.Instance.Info`.
150
151
151
152
**`behaviour do`** — declares which Ash actions should be wired for instance lifecycle management. Declaring `create :name` injects `:specified_by`, `:features`, and `:characteristics` arguments onto that action, and the `BuildBefore`/`BuildAfter` changes registered on `BaseInstance` automatically handle specification upsert, feature and characteristic creation, party validation, and graph relationship wiring for every create action. You write the action body for your domain-specific accepts and arguments; the structural wiring is handled for you.
152
153
@@ -208,6 +209,10 @@ defmodule Diffo.Compute.Cluster do
208
209
party :operator, Tenant
209
210
party :manager, Engineer
210
211
end
212
+
213
+
places do
214
+
place :data_centre, Diffo.Compute.DataCentre
215
+
end
211
216
end
212
217
213
218
behaviour do
@@ -507,6 +512,60 @@ defmodule Diffo.Compute.Engineer do
507
512
end
508
513
```
509
514
515
+
## Place Extension
516
+
517
+
`Diffo.Provider.BasePlace` is an Ash Resource Fragment for domain-specific Place kinds, mirroring `BaseInstance` and `BaseParty`. It provides common Place attributes — `id`, `href`, `name`, `type`, `referred_type` — and the `Diffo.Provider.Place.Extension` DSL, which lets a Place kind declare the roles it plays with respect to Instances, Parties, and other Places.
518
+
519
+
`type` defaults to `:PlaceRef` and is typically set in the `build` action to the concrete place type (`:GeographicSite`, `:GeographicLocation`, or `:GeographicAddress`). When `referred_type` is present, `type` must be `:PlaceRef` — meaning this Place is a reference rather than a physical location.
520
+
521
+
The `Diffo.Provider.Place.Extension` DSL cheat sheet is at [DSL-Diffo.Provider.Place.Extension](https://hexdocs.pm/diffo/DSL-Diffo.Provider.Place.Extension.html).
522
+
523
+
### Defining Place kinds
524
+
525
+
We'll add a `DataCentre` Place kind to our Compute domain. Clusters are hosted at a data centre; the `instances do` block records that relationship from the DataCentre's perspective.
526
+
527
+
```elixir
528
+
defmoduleDiffo.Compute.DataCentredo
529
+
@moduledoc """
530
+
DataCentre in the Compute domain
531
+
"""
532
+
533
+
aliasDiffo.Provider.BasePlace
534
+
aliasDiffo.Compute
535
+
536
+
useAsh.Resource,
537
+
fragments: [BasePlace],
538
+
domain:Compute
539
+
540
+
resource do
541
+
description "A Compute Data Centre"
542
+
plural_name :data_centres
543
+
end
544
+
545
+
jason do
546
+
pick [:id, :href, :name, :type]
547
+
compact true
548
+
rename type:"@type"
549
+
end
550
+
551
+
outstanding do
552
+
expect [:id, :name, :type]
553
+
end
554
+
555
+
actions do
556
+
create :builddo
557
+
accept [:id, :href, :name]
558
+
change set_attribute(:type, :GeographicSite)
559
+
end
560
+
end
561
+
562
+
instances do
563
+
role :data_centre, Diffo.Compute.Cluster
564
+
role :data_centre, Diffo.Compute.GPU
565
+
end
566
+
end
567
+
```
568
+
510
569
### Compute Domain
511
570
512
571
With all resources defined we can now declare the `Diffo.Compute` domain, which exposes a typed API for each resource:
@@ -591,27 +656,18 @@ alias Diffo.Provider.Instance.Party
591
656
592
657
### Creating a Cluster
593
658
594
-
We'll use a helper module to set up the data centre place:
659
+
First we create the data centre — our `DataCentre` resource uses `BasePlace`, so it is managed via the Compute domain API like any other domain resource:
Now build the cluster, passing the data centre as a place and our party members by id and role:
668
+
669
+
```elixir
670
+
places = [%Place{id: dc.id, role::data_centre}]
615
671
parties = [
616
672
%Party{id: tenant.id, role::operator},
617
673
%Party{id: engineer.id, role::manager}
@@ -682,10 +738,10 @@ What happens when I request a specific assignment from an instance to which the
682
738
683
739
### What Next?
684
740
685
-
In this tutorial you've used Diffo's Provider Instance Extension to define a Compute domain with a composite Cluster resource comprised of assigned GPU cores, and the Provider Party Extension to define Tenant and Engineer party kinds that operate and manage those resources.
741
+
In this tutorial you've used Diffo's Provider Instance Extension to define a Compute domain with a composite Cluster resource comprised of assigned GPU cores, the Provider Party Extension to define Tenant and Engineer party kinds that operate and manage those resources, and the Provider Place Extension to declare where instances and parties exist geographically.
686
742
687
-
`BaseParty`follows the same pattern as `BaseInstance` — domain-specific party resources use it as a fragment and write their own `build` action for domain-specific attributes. No manual wiring is needed.
743
+
`BaseParty`and `BasePlace` follow the same pattern as `BaseInstance` — domain-specific resources use them as fragments and write their own actions for domain-specific attributes. No manual wiring is needed.
688
744
689
-
A `BasePlace` extension for domain-specific Place kinds (such as a DataCentre with its own attributes) follows the same pattern and will be added in a future release.
745
+
Domain-specific Place kinds (such as a DataCentre with its own attributes) use `BasePlace` as a fragment and declare their roles via `instances do`, `parties do`, and `places do` sections on `Diffo.Provider.Place.Extension`. Party kinds similarly declare their place roles via `places do` on `Diffo.Provider.Party.Extension`.
690
746
691
747
If you find Diffo useful please visit and star on [github](https://github.com/diffo-dev/diffo/). Feel free to join discussions and raise issues to discuss PR's.
0 commit comments