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.

Reply via email to