On Thursday, September 22, 2016 at 11:24:38 PM UTC+1, Dénes Harmath wrote:
>
> in a typical model, there are cross-references: e.g. we have artists and
> albums, and the album refers to its artist. How do we model this in Elm? A
> possible approach is giving unique identifiers to objects and store the
> referred object's identifiers similar to the TodoMVC example - is this
> mechanism extracted to some library in a type-safe way?
>
Something I have been thinking about...
Thats sort of like building a database with foreign keys within your UI
code - but I can see you might have to do that.
Why not just:
type alias Album { name: String, artists : List Artist }
type alias Artist { name : String }
Album is the root of what I call a 'document fragment'.
Things get more complicated if you also need the Artist to hold a list of
Albums they performed on. You can have mutual recursion, but it requires
defining types, as type alias cannot recurse.
type Album =
Album { name : String, artists : List Artist }
type Artist =
Artist { name : String, albums : List Album }
In this case this isn;t going to work, because an artist will have a list
of albums they performed on, which will have a list including that artist,
which will have a list of albums they performed on, which ...
In the first case 'document fragment', I would consider the Album to be an
entity, by which I mean a persisted object with an explicit identifier
(maybe an int or a GUID). As the artist only appears within an album, it
does not need to be an entity; they have a relationship by composition.
Composition means that something is part of something else, its lifecycle
is tied to the parent. In this case, that also happens not to be true -
Artists might exist as artists before their first album (only released
singles), and artists appear in multiple albums. So are artists are
entities too. For the purposes of modelling them in a database, they are
definitely entities, but for the purpose of displaying information about
albums in a UI, the document fragment approach might be sufficient.
So modelling Artists and Albums as entities (with Int ids), with a
relationship by aggregation gives:
type Album =
Album { name : String, artists : List Int }
type Artist =
Artist { name : String, albums : List Int }
Which is a bit inconvenient to work with, as if I fetched an Album over
REST, I would then have to iterate the List, and make 1 REST call per
artist to get the list of artists.
What I have done (one the server side in Java), is to make all Entities
implement a Reference interface, which means that every entity also has a
degenerate form where it is simply represented by its id. When querying for
a document fragment containing child entities, I can choose how the 'slice'
the data. That is to say that, if I know I will use the entities I will
eagerly fetch them, if I don't expect to use them, I will just pull their
ids, and they can be lazily fetched as required.
Understanding the nature of the relationships in a data model and choosing
how to slice it is a major driver in designing an API to work with that
data model.
Coming back to artists and albums, how about the below. I'll use a String
id this time, in fact I think I will always use a String id, even if the id
was an Int, as the id is an identifier that the server side needs to
understand but for the UI it is just an opaque label that identifies
something.
type Album =
Album { name : String, artists : List Artist }
| Reference String
type Artist =
Artist { name : String, albums : List Album }
| Reference String
If I have an album query that fetches the album and its artists - I would
slice after that second level. So the artists would only hold references to
other albums (to link to them on the page), but not pull that data.
When working with this data model, when you hit a Reference, you need to
trigger a request to fetch it on demand.
One problem with the above, is that determining all references up front can
add to the cost of the query. In some cases I might not want to put in the
references at all, if I know I really will never use them. So perhaps:
type Album =
Album { name : String, artists : Maybe List Artist }
| Reference String
type Artist =
Artist { name : String, albums : Maybe List Album }
| Reference String
What do you think?
--
You received this message because you are subscribed to the Google Groups "Elm
Discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.