# Course subscriptions A runnable Gleam and Factos simulation of the [DCB course subscriptions example](https://dcb.events/examples/course-subscriptions/). ## Challenge Students subscribe to courses under constraints that cross traditional aggregate boundaries: - course identifiers must be unique; - a course cannot exceed its current capacity; - a student cannot subscribe to the same course twice; - a student cannot subscribe to more than five courses. Course capacity can also change after the course is defined. The source article uses a ten-course student limit. This compact runnable package sets the same configurable constraint to five. ## DCB approach `StudentSubscribedToCourse` is tagged with both `course:` and `student:`. A subscription command builds one decision context from two items: the course history needed to calculate capacity and occupancy, and the student's history needed to calculate their subscription count. Factos folds matching events into command-specific state. The tests use `factos/simulate` for deterministic scenarios, while the development app dispatches the same commands through `factos_sqlight` into in-memory SQLite. The package also demonstrates: - unique course definition; - course-capacity changes; - domain errors for missing, full, unchanged, and duplicate cases; - synchronized concurrent subscription attempts against the same course; - a JSON encoder returning `factos.Event(json.Json)` and a schema decoder selected by event type and version. The dispatch builder receives the encoder and decoder factory directly. Malformed payloads become `factos.DecodeError`; unsupported event types or versions become `factos.InvalidSchema`. The server component uses `lustre.application`; its `init` and `update` functions return managed Lustre effects instead of performing database work directly. Effects dispatch projection results back through messages. The command message wraps `course_subscription.Command`, so the UI exercises the domain model directly. Strong subscriptions update both projections in the same transaction as the event append. The command-to-state flow is diagrammed in the module documentation in [`src/course_subscription.gleam`](src/course_subscription.gleam). ## Run it Requirements: [Gleam](https://gleam.run/). Run the tests: ```sh gleam test ``` Run the Ewe development server, then open : ```sh gleam run -m course_subscriptions_dev ``` No database or container runtime is required. ## Package layout - [`src/course_subscription.gleam`](src/course_subscription.gleam) contains the commands, events, decision functions, DCB contexts, JSON codec, and model. - [`test/course_subscriptions_test.gleam`](test/course_subscriptions_test.gleam) verifies capacity, duplicate definition, and duplicate student scenarios. - [`dev/course_subscriptions_dev.gleam`](dev/course_subscriptions_dev.gleam) serves the document, Lustre runtime, and server-component WebSocket with Ewe. - [`dev/course_subscription_component.gleam`](dev/course_subscription_component.gleam) wraps domain commands in Lustre messages and renders the unstyled semantic UI. - [`dev/course_subscription_projection.gleam`](dev/course_subscription_projection.gleam) maintains and queries the in-memory SQLite projections.