Waher.IoTGateway. The backend is composed in TAG.Service.Parklet, exposed over HTTP through TAG.Service.Parklet.Http, and centered on domain services in TAG.Service.Parklet.Core.
Runtime shape
At startup,ParkletModule creates a ControllerEndpointRouter mounted at /parklet-api with SnakeCaseParameterNameCodec.
The module then:
- Registers account and anonymous authentication schemes on the router.
- Creates repositories, reference-data providers, domain services, hydrators, payment collaborators, entitlement services, and authorization services.
- Registers those dependencies in
router.ServiceProvider. - Creates
ParkletHttpService, which adds/open-api, request logging middleware, and scans the HTTP assembly for controllers. - Registers the router with
Gateway.HttpServer. - Starts background work such as event dispatching, reservation lifecycle tracking, debt payment scheduling, and Stripe subscription processing.
Stop() reverses the runtime pieces by unregistering the router and disposing or stopping the long-running workers.
Project layers
Request flow
An HTTP request enters throughGateway.HttpServer and is routed to /parklet-api/....
The HTTP router then:
- Matches the request method and route.
- Runs route middleware and authorization middleware in pipeline order.
- Creates a controller instance for the request through
router.ServiceProvider. - Binds route, query, and JSON body values using the router’s snake-case naming codec.
- Calls the controller action.
- Lets the controller call Core services and repositories through injected dependencies.
- Hydrates domain models into HTTP response DTOs and returns JSON.
ParkingAreaController is declared under ParkingAreasController, and ParkingSquaresController is declared under ParkingAreaController, producing routes such as /parklet-api/parking-areas/[areaId]/squares/[squareId]. The same pattern appears under /me, /groups/[groupId], and /principals/[principalId].
Authorization model
Parklet uses router authentication schemes to identify what kinds of callers the API accepts, then uses attribute-based authorization for endpoint permissions.ParkletAuthorization resolves request facts into an ABAC subject and context. ParkletAuthorizationMiddleware bridges the HTTP router authorization pipeline to the Parklet authorization service. The shared ParkletController base class gives controllers helpers for asserting the current principal, user, authorization state, and service results.
This keeps endpoint code focused on the use case while authorization policy, role lookup, and request fact resolution stay centralized.
Events and background work
Core services publish domain events throughIParkletEventPublisher. The default publisher writes events to a persisted DatabaseQueue, and PersistedQueueEventDispatcher dequeues events, discovers matching IParkletEventHandler<T> implementations through the runtime inventory, and invokes them with retry handling.
The module also starts specialized background workers:
- reservation lifecycle tracking, which advances reservation state based on runtime settings
- debt payment scheduling, which processes outstanding ledger debt through the payment gateway
- Stripe subscription event dispatching for lease subscription changes
OpenAPI endpoint
ParkletHttpService exposes /parklet-api/open-api as a live OpenAPI document endpoint.
It calls OpenApiGenerator.Generate(router, new OpenApiInfo { Title = "Parklet API", Version = "3.0.0" }), adds the current Neuron server URL with OpenApiGenerator.CurrentServers(), and returns the generated document as JSON.
The generated document reflects the current router tree, controller routes, bound request/response types, authentication metadata, authorization metadata, and OpenAPI annotations such as [Summary] and [Tag].
Where to add behavior
- Add new business rules in
TAG.Service.Parklet.Core. - Add storage behavior behind Core repository contracts in
TAG.Service.Parklet.Persistence. - Add external HTTP surface area in
TAG.Service.Parklet.Httpcontrollers, requests, responses, and hydrators. - Register new long-lived dependencies in
ParkletModule.Start()before controller discovery. - Publish cross-cutting side effects as domain events when the work should happen outside the request’s main transaction.