Courses
The courses are a domain of a studio that offers courses with a few seats each: the studio schedules its courses and calls a few off, and customers reserve seats, book them, pay, and sometimes cancel. In a request, the domain is named courses.
Subjects#
- A course is a subject of its own,
/courses/<uuid>. It guards its seats, since every rule about a seat asks how many of them are taken: reserving, booking, and releasing seats are events of the course. - A booking is a subject of its own,
/bookings/<code>, under a short code such asB3R2J5rather than a UUID, since a code is what people read out and type. It is aBand five characters from23456789ABCDEFGHJKMNPQRSTUVWXYZ, which leaves out what is easily misread. A booking holds its payments and how it ends. - A reservation is not a subject of its own, but a hold on seats of a course for a while, named by a
reservationIdwithin the course. - Customers have no events of their own. They appear only as references in the data, as
/customers/<uuid>inbookedBy.
For more on subjects, see Subjects.
Events#
| Event type | What it records | Data |
|---|---|---|
io.synthevents.courses.course-scheduled |
The studio offered a course with its sessions. | title, sessions, seats, fee |
io.synthevents.courses.seats-reserved |
Seats of a course are held for somebody who is about to book. | reservationId, seats, reservedAt, reservedUntil |
io.synthevents.courses.reservation-canceled |
Somebody gave a reservation up. | reservationId, canceledAt |
io.synthevents.courses.seats-booked |
The seats of a reservation were booked for a booking. | booking, seats, reservationId, bookedAt |
io.synthevents.courses.seats-released |
The seats of a canceled booking are free again. | booking, seats, releasedAt |
io.synthevents.courses.course-canceled |
The studio called a course off. | canceledAt |
io.synthevents.courses.booking-placed |
A booking was placed for a course. | course, bookedBy, seats, total, courseStartsAt, payableUntil, reservationId, placedAt |
io.synthevents.courses.booking-confirmed |
The seats of a booking are booked, so the booking holds. | confirmedAt |
io.synthevents.courses.booking-failed |
The seats of a booking could not be booked. | failedAt |
io.synthevents.courses.payment-received |
A payment for a booking came in. | amount, method, isFinal, receivedAt |
io.synthevents.courses.booking-paid |
A booking is paid. | paidAt |
io.synthevents.courses.booking-canceled |
A booking was canceled. | reason, canceledAt |
A booking runs over two subjects in four steps, since no command touches more than one: seats are reserved on the course, the booking is placed, the course books the reserved seats for it, and the booking is confirmed. If the seats cannot be booked, because the reservation lapsed and somebody else took them in the meantime, the booking fails instead. Canceling runs the other way round: the booking is canceled, and the course releases its seats.
Data#
titlenames the course.sessionslists its sessions in order, each withstartsAtandendsAt. A course begins with its first session.seatsis a number of seats: those a course offers, a reservation holds, or a booking takes.feeis what a seat costs,totalwhat a booking costs, andamountwhat a payment brings, each a sum of money in cents, with the unit and the currency written out, so that nobody takes the cents for euros:{"currency": "EUR", "unit": "cent", "value": 20000}.reservationIdnames a reservation within its course, as a UUID.coursenames a course as/courses/<uuid>,bookinga booking as/bookings/<code>, andbookedBya customer as/customers/<uuid>.courseStartsAtis when the course of a booking begins. The booking keeps it, since its rules see only their own subject.payableUntilis the day by which a booking is due to be paid, such as2025-01-14, in the time zone of the studio.methodsays how a payment came in:transfer,cash,online, orother.isFinalsays whether the studio takes the payment as settling the booking, even if the sum falls short of the total.reasonsays why a booking was canceled:requested,course-canceled, orunpaid.reservedAt,canceledAt,bookedAt,releasedAt,placedAt,confirmedAt,failedAt,receivedAt, andpaidAtare business times, in UTC to the millisecond, such as2025-01-07T15:47:45.184Z. They give the moment of the event, written the way an application usually captures it, which keeps them apart from the event's owntime, which has nanoseconds.reservedUntil,courseStartsAt, and the times of the sessions are written the same way.
Every field is present in every event of its type, and no other field is. The backup for EventSourcingDB carries the schemas that say so.
A booking-placed event, formatted for reading, looks like this:
{
"specversion": "1.0",
"id": "4",
"source": "tag:synthevents.io,2026:synthevents;version=golden;domain=courses;scenario=everyday;from=2025-01-01T00:00:00Z;to=2026-01-01T00:00:00Z;eventCount=500;seed=golden",
"subject": "/bookings/B9R8D5",
"type": "io.synthevents.courses.booking-placed",
"time": "2025-01-07T15:47:45.184613994Z",
"datacontenttype": "application/json",
"data": {
"bookedBy": "/customers/358c6e2b-dfec-4b16-a919-7999e4ede485",
"course": "/courses/f943a4d8-a841-477c-b540-128f13583bfd",
"courseStartsAt": "2025-02-06T16:00:00.000Z",
"payableUntil": "2025-01-14",
"placedAt": "2025-01-07T15:47:45.184Z",
"reservationId": "469cdebd-e899-4cda-8c27-d2f8c2585e7c",
"seats": 3,
"total": {
"currency": "EUR",
"unit": "cent",
"value": 60000
}
}
}
Note that this event comes from a test of SynthEvents, which generates its data sets with the version golden and the seed golden. In a data set of the public service, version names the version of SynthEvents that generated it.
Rules#
The events follow the rules of the studio. A command that breaks one of them is refused and leaves no event.
Courses#
- Scheduling offers a course with a title, at least one session, at least one seat, and a fee of more than nothing. Its sessions lie ahead, come in order without overlapping, and each ends after it begins.
- What takes seats or calls the course off ends with its first session: seats can be reserved and booked only before, a booking can be canceled only before, and the course can be called off only before. What only gives seats back does not end: a reservation can be given up and the seats of a booking released at any time, since they follow something that happened in time.
- A course that is called off takes no new reservations and no new bookings, but still releases seats.
Seats#
- The free seats of a course are its seats, minus those booked and not released, minus those held by reservations that still hold.
- Reserving holds seats until
reservedUntil, a moment the studio sets. When it passes, the seats are free again, without an event saying so. A reservation that lapsed can still be booked, as long as enough seats are free. - Booking seats takes the seats of a reservation, and books seats for a booking only once.
Bookings#
- Placing a booking needs a code that is not taken: a code drawn twice is refused, and the customer draws another one. A booking takes at least one seat, costs more than nothing, and is placed before its course begins.
payableUntilfollows from the studio's policy: a number of days after the day the booking is placed, and fewer when the course begins soon. No rule depends on it; whether a booking is overdue follows from it and the time.- Only a booking that is placed and still open can be confirmed or fail.
Payments#
- A payment comes in for a confirmed booking that is neither paid nor canceled, and brings more than nothing.
- When the payments reach the total, or when the studio takes a payment as final,
booking-paidfollowspayment-received, with the same time, as what follows from it. A payment that brings more than is open is taken as it is.
Cancellations#
- Canceling cancels a confirmed booking as a whole, until its course begins, also when it is paid. Whether money goes back is not part of the domain.
- When the studio calls a course off, it first cancels the course and then each of its bookings, with the reason
course-canceled. - The reason is told, not checked: the rules of a booking cannot see its course.
The Studio's Policy#
The moment, the end of a reservation, and the day a booking is due are the studio's to set, not the customer's. The rules go by a policy: how long a reservation holds, how many days a booking has to be paid and how many when its course begins soon, the time zone of the studio, and its clock. They take every business time from the clock and reckon every day in the time zone. The policy is set by the scenario.
Every command except scheduling and placing decides on the state as it is now, as a desk does: a seat is refused because of what is true of the course, not because something happened to it in between.
Scenarios#
- Pottery Studio in Everyday Operation (
everyday) is a pottery studio with its customers.