This chapter is the practical map of the four modules: what each one owns, which endpoints it exposes, which messages it publishes or consumes, and what makes it special.
The Users module is the system's internal identity and authorization source.
It owns:
- local user profiles
- the mapping between a Keycloak identity and the local user record
- roles
- permissions
It does not replace Keycloak as the identity provider. Instead, it complements Keycloak with application-specific data and authorization rules.
| Endpoint | Purpose |
|---|---|
POST users/register |
register a user in Keycloak and create the local user |
GET users/{id}/profile |
get a user profile |
GET users/my-profile |
get the current authenticated user's profile |
PUT users/{id}/profile |
update profile data |
UserRegisteredIntegrationEventUserProfileUpdatedIntegrationEvent
GetUserPermissionsRequestvia MassTransit request/response
- New users are created with the
Memberrole. - Permissions are derived from role-to-permission mappings stored in the module's own schema.
- This module is the source of truth for authorization data used by other modules.
The Events module is the authoritative source for the event catalog.
It owns:
- categories
- events
- ticket types
- event publication lifecycle
- event cancellation coordination
| Endpoint | Purpose |
|---|---|
POST categories |
create a category |
GET categories |
list categories |
GET categories/{id} |
get a category |
PUT categories/{id} |
update a category |
DELETE categories/{id}/archive |
archive a category |
POST events |
create an event |
GET events |
list events |
GET events/{id} |
get an event |
GET events/search |
search published events |
PUT events/{id}/publish |
publish an event |
PUT events/{id}/reschedule |
reschedule an event |
DELETE events/{id}/cancel |
cancel an event |
POST ticket-types |
create a ticket type |
GET ticket-types |
list ticket types for an event |
GET ticket-types/{id} |
get a ticket type |
PUT ticket-types/{id}/price |
change ticket price |
EventPublishedIntegrationEventEventRescheduledIntegrationEventEventCanceledIntegrationEventTicketTypePriceChangedIntegrationEventEventCancellationStartedIntegrationEventEventCancellationCompletedIntegrationEvent
EventPaymentsRefundedIntegrationEventEventTicketsArchivedIntegrationEvent
- An event can only be published if it has at least one ticket type.
- The module uses a MassTransit saga state machine to coordinate cancellation across modules.
- The saga state is stored in Redis when available, with an in-memory fallback.
The Ticketing module owns the commercial workflow.
It manages:
- customers
- carts
- orders
- payments
- local event and ticket-type copies needed for selling
- issued tickets
| Endpoint | Purpose |
|---|---|
PUT carts/add |
add a ticket type to the current cart |
PUT carts/remove |
remove a ticket type from the current cart |
DELETE carts |
clear the cart |
GET carts |
get the current cart |
POST orders |
create an order from the current cart |
GET orders |
list the current user's orders |
GET orders/{id} |
get order details |
GET tickets/{id} |
get ticket details |
GET tickets/code/{code} |
get a ticket by code |
GET tickets/order/{orderId} |
list tickets for an order |
OrderCreatedIntegrationEventTicketIssuedIntegrationEventTicketArchivedIntegrationEventTicketTypeSoldOutIntegrationEventEventPaymentsRefundedIntegrationEventEventTicketsArchivedIntegrationEvent
UserRegisteredIntegrationEventUserProfileUpdatedIntegrationEventEventPublishedIntegrationEventEventCancellationStartedIntegrationEventTicketTypePriceChangedIntegrationEvent
Customeris a local projection of a user, not a direct dependency on theUsersmodule.- Checkout uses pessimistic locking when reserving ticket-type inventory.
- Payment charging is intentionally faked in the sample.
- Ticket issuance happens after order creation through domain-event handlers.
The Attendance module owns check-in and attendance reporting.
It manages:
- attendees
- local event copies used for attendance operations
- local ticket copies used for check-in
- event statistics projections
| Endpoint | Purpose |
|---|---|
PUT attendees/check-in |
check a ticket in for an attendee |
GET event-statistics/{eventId} |
get attendance statistics for an event |
UserRegisteredIntegrationEventUserProfileUpdatedIntegrationEventEventPublishedIntegrationEventTicketIssuedIntegrationEvent
There are no major cross-module business integrations currently emitted from Attendance.
- Attendance maintains a MongoDB projection for event statistics.
- A bad check-in attempt does not silently fail. It raises domain events that update statistics.
- The module tracks duplicate and invalid check-in attempts, not just successful ones.
| Business Concept | Owning Module |
|---|---|
| user profile | Users |
| roles and permissions | Users |
| event category | Events |
| event catalog | Events |
| ticket type definition and price | Events |
| cart | Ticketing |
| order | Ticketing |
| payment | Ticketing |
| issued ticket for selling | Ticketing |
| attendee check-in state | Attendance |
| attendance statistics | Attendance |
When you move through the module code, pay special attention to these repeated patterns:
- endpoints call MediatR
- commands update aggregates and save through a module-specific unit of work
- aggregates raise domain events
- domain events are processed later through the outbox
- integration events enter through inbox consumers and presentation handlers
Once you recognize that rhythm, the repository becomes much easier to navigate.