Skip to content

Commit

Permalink
Changed strategy for LOS and LOI IDs
Browse files Browse the repository at this point in the history
  • Loading branch information
wrygiel committed Jan 9, 2017
1 parent a28e2b2 commit e66750d
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 55 deletions.
64 changes: 20 additions & 44 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,6 @@ has not been conducted in the searched timespan.

### LO identifiers: `los_id` and `loi_id`

**Please read it carefully. Hasty decisions might cost you in the future!**

There are major differences in a way learning opportunities are modeled in
various computer systems:

Expand All @@ -71,48 +69,26 @@ various computer systems:
there exists a `Course`, and a `Degree Programme` with exactly the same
primary key.

In EWP, we have decided to adopt the former strategy (as EMREX had). **This
means that partners using the latter strategy (multiple tables) might need to
perform some additional actions in order to avoid ID conflicts:**

* One way of dealing with the problem would be to force unique IDs across
multiple tables. This way, whenever you receive an ID, you will be able to
find out which entity it refers to (by searching multiple tables). This
approach however is often bad for performance reasons (and most databases
don't support it natively). Also, you might be required to update your
existing primary keys, which is bad in itself.

* Another way would be to use UUIDs as `los_id`. You won't need to update your
primary keys, but you will need to keep another set of unique keys, which
is also not so good for performance.

* Fastest approach (albeit a bit more complex) is to use "dynamic IDs". You
do not change your database in any way, but you are required to publish
unique, dynamically generated IDs in your EWP responses. Clients will treat
such "dynamic IDs" as regular IDs. Later, whenever a client asks about the
details of such ID, you must be able to dynamically map it back into a real
row in a real table.

**Example:** if you have separate tables for degree programmes and for
courses, and you have a course with a primary key value of `123`, then,
whenever you refer to this course in EWP APIs, you might use `C123`
(instead of just `123`). Later, if one of the clients asks you about `P123`,
you will know that he is after a `Degree Programme`, not a `Course`, and you
will know which of your tables to query for `123`.

Note, that letters `C` and `P` are just examples. From the client's point of
view, they are part of the ID, and they have no special meaning. It is up to
you to decide on this meaning. **Once you decide on one schema, you will
need to use it consistently across all EWP APIs.**

Same is true for `loi_id` values (which are unique identifiers of specific
LOIs).

Note, that you SHOULD make this decision now, even if you only expose a single
type of learning opportunity via this API. If you don't, then you will have
problems expanding to other types in the future.

Also, IDs of your LOSes and LOIs generally **shouldn't change**.
In EWP, we have considered many ways of dealing with this issue, and eventually
we have [decided]
(https://github.com/erasmus-without-paper/ewp-specs-api-mobilities/issues/9) to
adopt the somewhat "mixed" strategy - we use a single set of IDs for all LOS
types, but **the type of each LOS MUST be included in its ID, in a clearly
specified way** (see XSD for details). Same is true for `loi_id` values (which
are unique identifiers of specific LOIs).

There are a couple of consequences of this strategy:

* Each exposed LOS MUST have type.

* LOSes MUST NOT change their type, ever. If, for some reason, HEI wants to
change a type of the LOS entity, it MUST expose is under a different ID.

* For safety, it is HIGHLY RECOMMENDED that all implementers use [surrogate
IDs](https://en.wikipedia.org/wiki/Surrogate_key) in their LOS ID suffixes.
Our recommendation is to use UUIDs, but other surrogate keys are also
acceptable. This recommendation is also true for all other IDs exposed via
EWP.


Request method
Expand Down
14 changes: 6 additions & 8 deletions response-example.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
solely on the example file.
-->
<learningOpportunitySpecification>
<los-id>MA100</los-id>
<los-id>CR/MA100</los-id>
<title xml:lang="no">Grunnkurs i matematikk</title>
<title xml:lang="en">Introductory calculus</title>
<type>Course</type>
Expand All @@ -24,11 +24,9 @@
<url>http://example.com/</url>
<description xml:lang="no">En eller annen beskrivelse</description>
<description xml:lang="en">Some description</description>
<!-- <grading-scheme>ects</grading-scheme> -->
<!-- <eqf-level>1</eqf-level> -->
<specifies>
<learningOpportunityInstance>
<loi-id>2016/2017-MA100</loi-id>
<loi-id>CRI/2016/2017-MA100</loi-id>
<start>2016-07-01</start>
<end>2016-12-31</end>
<trm:academic-term>
Expand Down Expand Up @@ -60,18 +58,18 @@
<engagementHours>60</engagementHours>
</learningOpportunityInstance>
<learningOpportunityInstance>
<loi-id>2015/2016-MA100</loi-id>
<loi-id>CRI/2015/2016-MA100</loi-id>
<start>2015-07-01</start>
<end>2015-12-31</end>
</learningOpportunityInstance>
</specifies>
<contains>
<los-id>MA100-A</los-id>
<los-id>MA100-B</los-id>
<los-id>CLS/MA100-A</los-id>
<los-id>CLS/MA100-B</los-id>
</contains>
</learningOpportunitySpecification>
<learningOpportunitySpecification>
<los-id>STAVH</los-id>
<los-id>DEP/STAVH</los-id>
<title xml:lang="no">Statsvitenskap hovedfag</title>
<title xml:lang="en">Political science, main subject</title>
<type>Degree Programme</type>
Expand Down
70 changes: 67 additions & 3 deletions response.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="los-id" minOccurs="1" maxOccurs="1" type="xs:string">
<xs:element name="los-id" minOccurs="1" maxOccurs="1" type="LosID">
<xs:annotation>
<xs:documentation>
Unique identifier of this LOS. Will always match one of the `los_id` values
Expand Down Expand Up @@ -217,7 +217,7 @@
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="loi-id" minOccurs="1" maxOccurs="1" type="xs:string">
<xs:element name="loi-id" minOccurs="1" maxOccurs="1" type="LoiID">
<xs:annotation>
<xs:documentation>
Unique identifier of this LOI.
Expand Down Expand Up @@ -424,7 +424,7 @@
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name="los-id" type="xs:string" minOccurs="0" maxOccurs="unbounded">
<xs:element name="los-id" type="LosID" minOccurs="0" maxOccurs="unbounded">
<xs:annotation>
<xs:documentation>
A list of IDs of LOSes which are considered to be "part of" this LOS. For
Expand All @@ -446,4 +446,68 @@
</xs:complexType>
</xs:element>

<xs:simpleType name="LosID">
<xs:annotation>
<xs:documentation>
Unique local identifier of Learning Opportunity Specification. These IDs take
form of a "Type/X" string, where "Type" is a short code of the LOS'es type, and
"X" is the internal surrogate key of the LOS, as exposed by the owner HEI
(prefferably an UUID, but any other values are also permitted).

Read a detailed introduction here:
https://github.com/erasmus-without-paper/ewp-specs-api-courses#unique-identifiers
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="(CR|CLS|MOD|DEP)/(.{1,40})">
<xs:annotation>
<xs:documentation>
The actual internal ID of the LOS entity MUST be prefixed with one of the
following strings:

- "CR/" - if it is a "Course" type LOS,
- "CLS/" - if it is a "Class" type LOS,
- "MOD/" - if it is a "Module" type LOS,
- "DEP/" - if it is a "Degree Programme" type LOS.

More prefixes MAY be introduced in the future. Clients SHOULD expect that (e.g.
they should accept unknown prefixes and behave as if the referenced entity did
not exist).
</xs:documentation>
</xs:annotation>
</xs:pattern>
</xs:restriction>
</xs:simpleType>

<xs:simpleType name="LoiID">
<xs:annotation>
<xs:documentation>
Unique local identifier of Learning Opportunity Instance. This is very similar
to LosID type, but it is used for instances (not specifications).

Read a detailed introduction here:
https://github.com/erasmus-without-paper/ewp-specs-api-courses#unique-identifiers
</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string">
<xs:pattern value="(CRI|CLSI|MODI|DEPI)/(.{1,40})">
<xs:annotation>
<xs:documentation>
The actual internal ID of the LOS entity MUST be prefixed with one of the
following strings:

- "CRI/" - if it is an instance of "Course" type LOS,
- "CLSI/" - if it is an instance of "Class" type LOS,
- "MODI/" - if it is an instance of "Module" type LOS,
- "DEPI/" - if it is an instance of "Degree Programme" type LOS.

More prefixes MAY be introduced in the future. Clients SHOULD expect that (e.g.
they should accept unknown prefixes and behave as if the referenced entity did
not exist).
</xs:documentation>
</xs:annotation>
</xs:pattern>
</xs:restriction>
</xs:simpleType>

</xs:schema>

2 comments on commit e66750d

@kaiqu
Copy link

@kaiqu kaiqu commented on e66750d Jan 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, but I think the comments also should say something to the effect that "the important thing is that the IDs must be stable over time, for future reference".

@wrygiel
Copy link
Collaborator Author

@wrygiel wrygiel commented on e66750d Jan 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Replied here.

Please sign in to comment.