classDiagram direction TB class AggregationJob { -ObservationQuery query +runAll() void -countSamples() void -computeSeatDrop() void -computeCellRisk() void } class ObservationQuery { <> +upsertSampleCount() int +upsertSeatDrop() int +upsertCellRisk() int } class CellRisk { -int stopOrder -TimeBand timeBand -double risk -int sampleSize +risk() double +hasEnoughSamples() boolean } class CellRiskRepository { <> +findByRoute(String routeId, TimeBand band) List~CellRisk~ } class ForecastJob { -RouteForecaster forecaster -RouteForecastRepository forecasts -List~String~ routeIds -Clock clock +publishAll() void } class RouteForecaster { -ChanceModel model -CellRiskRepository risks +forecast(String routeId, Instant judgedAt) RouteForecast } class ChanceModel { <> +version() String +estimate(int stopOrder, TimeBand band, CellRisk risk) ChanceState } class BaselineChanceModel { +version() String +estimate(int stopOrder, TimeBand band, CellRisk risk) ChanceState } class ChanceState { <> +writeTo(RouteForecast f, int stopOrder) void } class Estimated { -Probability probability +writeTo(RouteForecast f, int stopOrder) void } class NotEstimated { -Reason reason +writeTo(RouteForecast f, int stopOrder) void } class Probability { -double value +of(double v) Probability +percent() int } class Reason { <> UNAVAILABLE } class TimeBand { <> MORNING_RUSH OTHER +of(LocalTime local) TimeBand } class RouteForecast { -Long id -String routeId -Instant judgedAt -String modelVersion -List~BoardingChance~ stops +startedAt(String routeId, Instant at, String version) RouteForecast +addEstimated(int stopOrder, int percent) void +addUnavailable(int stopOrder, Reason reason) void } class BoardingChance { -Long id -int stopOrder -Integer percent -Reason reason } class RouteForecastRepository { <> +save(RouteForecast f) RouteForecast } AggregationJob --> ObservationQuery ObservationQuery ..> CellRisk ForecastJob --> RouteForecaster ForecastJob --> RouteForecastRepository RouteForecaster --> ChanceModel RouteForecaster --> CellRiskRepository RouteForecaster ..> RouteForecast CellRiskRepository ..> CellRisk ChanceModel <|.. BaselineChanceModel ChanceModel ..> ChanceState ChanceState <|.. Estimated ChanceState <|.. NotEstimated Estimated --> Probability NotEstimated --> Reason ChanceState ..> RouteForecast RouteForecast o-- BoardingChance