Docs / SynthEvents / Fundamentals / Courses

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 as B3R2J5 rather than a UUID, since a code is what people read out and type. It is a B and five characters from 23456789ABCDEFGHJKMNPQRSTUVWXYZ, 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 reservationId within the course.
  • Customers have no events of their own. They appear only as references in the data, as /customers/<uuid> in bookedBy.

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#

  • title names the course. sessions lists its sessions in order, each with startsAt and endsAt. A course begins with its first session.
  • seats is a number of seats: those a course offers, a reservation holds, or a booking takes.
  • fee is what a seat costs, total what a booking costs, and amount what 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}.
  • reservationId names a reservation within its course, as a UUID.
  • course names a course as /courses/<uuid>, booking a booking as /bookings/<code>, and bookedBy a customer as /customers/<uuid>.
  • courseStartsAt is when the course of a booking begins. The booking keeps it, since its rules see only their own subject.
  • payableUntil is the day by which a booking is due to be paid, such as 2025-01-14, in the time zone of the studio.
  • method says how a payment came in: transfer, cash, online, or other. isFinal says whether the studio takes the payment as settling the booking, even if the sum falls short of the total.
  • reason says why a booking was canceled: requested, course-canceled, or unpaid.
  • reservedAt, canceledAt, bookedAt, releasedAt, placedAt, confirmedAt, failedAt, receivedAt, and paidAt are business times, in UTC to the millisecond, such as 2025-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 own time, 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.
  • payableUntil follows 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-paid follows payment-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#