-
Couldn't load subscription status.
- Fork 1.1k
Description
IBI wants to start using fares in the GraphQL API and in particular some Fares V2 features.
Personally, I'm quite dissatisfied with how fares are represented in the API. Sadly, when implementing Fares V2 I made the situation worse.
Problems with the current approach
- The current fares API focuses on "a price" rather than on purchasable tickets.
- It combines several distinct concepts (rider categories, fare containers) into a single "type" leading to ever more types like
electronicYouth,electronicSenior... - The so-called fare component is rather awkward to use and the cross-references to the actual legs is difficult to resolve.
Fares V2
Fares V2 has made some nice conceptual advances that I think we should make use of in the API:
- Focus on "fare products" which are purchasable tickets
- Fare containers like app, cash, RFID card...
- Rider categories for students, seniors, veterans....
API design
If we agree that we want to design the API around those concepts, then we have to resolve several cases which are not so easy to represent:
- a fare product covers the entire itinerary
- a fare product covers a single leg
- a fare product covers several legs, but not all of them
Possible approaches
Separate fields
One would be tempted to have separate fields for those fare products that cover an itinerary and those that cover a leg on those entities respectively. This would be easy to implement but has the following drawbacks:
- the consumer has to look in two places to find all the fare products
- you still need cross-references to find those fares that cover several, but not all legs
One place to find fare products (but with higher complexity)
Another approach is to have the fares only on the legs but adding a cross-reference id to each fare product. That would mean that the client has to look at the cross-reference id to figure out if a fare product applies to one/several legs or the entire itinerary.
Makes processing the easy case (single fare product for entire itinerary) more complex to process but its upside is that it's a universal API that works for all cases and it only returns the fare information in a single place.
Proposed schema
"Category of riders a fare product applies to, for example students or pensioners."
type RiderCategory {
"ID of the category"
id: String!
"Human readable name of the category."
name: String
}
"""
A 'container' that a fare product applies to, for example 'Oyster Card' or 'DB Navigator App'.
"""
type FareContainer {
"ID of the container"
id: String!
"Human readable name of the container."
name: String
}
"A fare product (ticket) to cover the entire or a subset of an itinerary."
type FareProduct {
"""
Identifier local to the itinerary that allows to cross-reference fare products that span
more than one leg.
If you want to uniquely identify the fare product itself (not its instance) use `id`.
### Example: Day pass
When a day pass is valid for all three legs in the itinerary it will appear
for each leg but with the same `instanceId`.
*It is the responsibility of the API consumers to display the day pass as a product for the
entire itinerary rather than three day passes!*
### Example: Several single tickets
If you have two legs and need to buy two single tickets they will appear in each leg with the
same `id` but different `instanceId`.
"""
instanceId: String!
"Identifier for the fare product."
id: String!
"""
Human readable name of the product, for example example "Day pass" or "Single ticket".
"""
name: String!
"The price of the product"
price: Money!
"The category of riders this product applies to, for example students or pensioners."
riderCategory: RiderCategory
"""
The 'container' that this product applies to, for example "Oyster Card" or "Berlin Ticket App".
This communicates to riders that a specific way of buying or keeping this product is required.
"""
container: FareContainer
}
type Leg {
...
"""
Fare products are purchasable tickets which may have an optional fare container or rider
category that limits who can buy them or how.
Please read the documentation of `instanceId` very carefully to learn how a single fare product
that applies to multiple legs can appear several times.
"""
fareProducts: [FareProduct]
}Community discussion
Since I'm quite unhappy with how I implemened Fares V2 in the REST API, I would like to take the time to involve the community before going ahead with this.