- TypeScript 52.7%
- Python 47.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| python | ||
| ts | ||
| .gitignore | ||
| README.md | ||
| skyspell-ignore.toml | ||
Train Reservation with a pentagonal architecture
Goal
Learn about the hexagonal architecture, by using tests to drive the refactorings.
Also, learn the difference betweed a CRUD app (which only does create, read, update, delete) and an app that has business logic.
Context
You are implementing a back end API to book seats from trains.
Each train has an ID.
Each train contains coaches, identified with letters (A, B, C ...)
Each coach contain seats, which have a number containing the coach letter : "1A", "2A", and so on.
Each seat also has an optional booking reference (depending if they are free or already booked).
For now, two routes are implemented:
GET /trains
and
GET /trains/<name>/seats
Instructions
First, choose a programming language, and check you can :
- run the tests
- call the
/trainsand/trains/express_2000/seatsroutes (usingcurlfor instance)
Part 1
Answer the following questions:
- Are you confident that the code is working as expected ?
- Do you think there are missing tests ?
- Do you think the architecture is the right one ?
Part 2
Create a branch, and implement a new feature: the GET /trains/{name}/seats route
should also return the booking reference of each seats, like this:
GET /trains/express_2000/seats
{
"seats": [
{ "number": "1A", "booking_reference": "abc123" },
{ "number": "2A", "booking_reference": "abc123" },
{ "number": "3A", "booking_reference": "" },
{ "number": "4A", "booking_reference": "" },
]
}
Make sure the tests still pass.
What do you think about the way the test code changed ?
Part 3
Go back to the main branch, and rewrite the tests to not use any mocks.
Part 4
Re-implement the same feature as in part 2
Compare with the tests you had at the end of part 2.
Part 5
Implement the POST /book route:
The body should look like:
{
"train_id": "express_2000",
"seats": ["1A", "2A"],
"booking_reference": "abc123def"
}
Note that the server must prevent you from booking a seat that is already reserved with another booking reference, by returning a "409 conflict" status code.
It is however OK to try and book the same seat with twice with the same booking reference.
Implement this new route. Is the app still a CRUD app ?
Part 6
First off, your users won't know their seat numbers in advance !
So the POST /book should instead take a seat count:
{
"train_id": "express_2000",
"seat_count": 2,
"booking_reference": "abc123def"
}
(you can assume the booking reference is fetched form an other source at this point)
Your code should then find some free seats and return them::
{
"booked": ["1A", "2A"]
}
or return an error message if no free seats are available.
Part 7
You need to implement more business rules:
- It should not be possible to book seats that are not in the same coach.
For instance, trying to book seat "1A" and "1B" should fail.
- The app should try and maintain 70% occupancy for each coach
For instance, let's say each coach has 100 seats, and 80% of the seats in coach A are booked, but all the seats in coach B are free. Then, after trying to book 2 seats you should return 2 seats from coach B.
This time, it should be clear you app is no longer just a CRUD - time to do some serious refactoring :)