From Single Events to Multi-Stop Journeys
Events rarely happen in isolation. A weekend retreat involves travel, accommodation, activities at multiple locations, and coordinated transport. A conference trip might span several cities over a week. When we built the trip module for Play The Event, the goal was to give organizers a complete planning tool that goes far beyond a simple itinerary list.
This article explores how the trip management system works, from the domain model to the external API integrations that enrich the planning experience with real-world data.
What You Will Learn in This Article
- How the trip module models multi-stop itineraries
- Activities, accommodations, and transport management
- External API integration: Nominatim, WikiVoyage, and Foursquare
- Interactive maps with MapLibre GL
- Budget tracking per trip segment
- The relationship between trips and events in the domain
Trip Data Model
A trip in Play The Event is modeled as a hierarchical structure. At the top level sits the Trip entity, which is always associated with an event. A trip contains one or more Stops, each representing a distinct location the group will visit. Every stop can then hold Activities, Accommodations, and Transports.
TRIP MODEL
Trip (aggregate root)
├── id: UUID
├── eventId: UUID (parent event)
├── name: String
├── startDate / endDate: LocalDate
├── status: DRAFT | PLANNED | IN_PROGRESS | COMPLETED
│
├── stops: List<Stop>
│ ├── Stop #1
│ │ ├── location: GeoPoint (lat, lng)
│ │ ├── address: String
│ │ ├── arrivalDate / departureDate
│ │ ├── activities: List<Activity>
│ │ │ ├── name, description, time, duration
│ │ │ ├── cost: Money
│ │ │ └── externalRef: FoursquareVenueId?
│ │ ├── accommodations: List<Accommodation>
│ │ │ ├── name, address, checkIn/checkOut
│ │ │ ├── costPerNight: Money
│ │ │ └── bookingReference: String?
│ │ └── transports: List<Transport>
│ │ ├── type: FLIGHT | TRAIN | BUS | CAR | FERRY
│ │ ├── departure / arrival locations
│ │ ├── departureTime / arrivalTime
│ │ └── cost: Money
│ ├── Stop #2
│ │ └── ...
│ └── Stop #N
│
└── totalBudget: Money (calculated)
This model ensures that every aspect of a multi-stop trip is captured in a structured way. Organizers can plan the complete journey, assign costs to specific segments, and share the itinerary with all participants.
Activities Management
Each stop can have multiple activities scheduled. An activity represents anything the group plans to do at that location: visiting a museum, attending a workshop, having dinner at a specific restaurant, or going on a guided tour.
Activities carry scheduling information (date, time, estimated duration), cost data, and optional external references. When an organizer searches for things to do at a stop, the platform queries the Foursquare API to suggest nearby venues, restaurants, and points of interest.
Activity Features
- Time-slot scheduling with conflict detection
- Per-activity budget with automatic rollup to the trip total
- Optional participant subset (not everyone must join every activity)
- Notes and links for booking confirmations
- Foursquare venue linking for ratings, photos, and directions
Accommodations and Transport
For each stop, organizers can record accommodation details including the property name, address, check-in and check-out dates, cost per night, and booking references. The system calculates the total accommodation cost based on the number of nights and the number of rooms booked.
Transport segments connect consecutive stops. Each transport entry specifies the mode of travel (flight, train, bus, car, or ferry), departure and arrival locations and times, and the associated cost. This gives participants a complete picture of how they will move from one stop to the next.
TRIP: "Southern Italy Road Trip"
Stop 1: Naples (3 nights)
└── Transport → Stop 2: TRAIN
├── Departure: Naples Centrale, 09:15
├── Arrival: Salerno, 10:00
└── Cost: EUR 12.00 per person
Stop 2: Amalfi Coast (2 nights)
└── Transport → Stop 3: CAR (rental)
├── Departure: Salerno
├── Arrival: Matera
└── Cost: EUR 85.00 (fuel + toll)
Stop 3: Matera (2 nights)
└── Transport → Return: FLIGHT
├── Departure: Bari Airport, 18:30
├── Arrival: Milan Malpensa, 20:15
└── Cost: EUR 65.00 per person
External API Integrations
One of the most powerful aspects of the trip module is its integration with external APIs that provide real-world data to enrich the planning experience. Rather than asking organizers to manually enter every detail, the platform queries multiple services to offer suggestions, auto-complete addresses, and display relevant information.
Nominatim (OpenStreetMap Geocoding)
Nominatim powers the address search and geocoding functionality. When an organizer types a location name, the platform sends a query to Nominatim and returns matching results with full addresses and geographic coordinates. These coordinates are then used to place markers on the map and calculate distances between stops.
WikiVoyage
For each stop, the platform queries the WikiVoyage API to retrieve travel guides and destination information. This gives organizers access to community-curated content about what to see, where to eat, and practical travel tips, directly within the planning interface.
Foursquare Places API
The Foursquare Places API provides venue search capabilities. When organizers are looking for restaurants, attractions, or services near a stop, the platform queries Foursquare and displays results with ratings, categories, photos, and contact information. Selected venues can be directly added as activities.
API Rate Limiting and Caching
To avoid exceeding API rate limits and to improve response times, all external API responses are cached at the backend level. Geocoding results are cached indefinitely (locations do not change), while Foursquare venue data is cached for 24 hours. WikiVoyage content is cached for 7 days. This strategy reduces external calls by approximately 80% during active planning sessions.
Interactive Maps with MapLibre GL
The trip planner includes an interactive map powered by MapLibre GL, an open-source map rendering library. The map displays all stops as markers, draws the route between them, and allows organizers to visually plan the journey.
Key map features include:
- Stop markers with numbered labels showing the visit order
- Route lines connecting consecutive stops
- Activity pins showing planned activities around each stop
- Clickable markers that open detail panels with accommodation, transport, and activity information
- Drag-and-drop reordering of stops directly on the map
Using MapLibre rather than proprietary solutions like Google Maps keeps the platform free from per-request pricing and allows full customization of the map style to match the application design system.
Budget Tracking Per Segment
Every element of a trip carries a cost, and the system aggregates these costs at multiple levels. At the activity level, the organizer sees individual costs. At the stop level, all activities, accommodations, and incoming transport costs are summed. At the trip level, the total budget is calculated across all stops.
This hierarchical budget view allows organizers to quickly identify which segments are the most expensive and make adjustments. Combined with the expense module described in the previous article, costs can be automatically converted into shared expenses with the appropriate splitting mode applied.
Trip-to-Expense Integration
- One-click conversion from trip cost items to shared expenses
- Automatic participant assignment based on who joins each activity
- Multi-currency handling inherited from the expense module
- Budget vs. actual comparison as expenses are recorded during the trip
Key Takeaways
Lessons from Building the Trip Module
- Hierarchical data models: Trips, stops, activities, and transports form a natural tree that maps cleanly to both the domain and the UI
- External APIs add massive value: Nominatim, WikiVoyage, and Foursquare transform a basic planner into an intelligent assistant
- Caching is essential: Aggressive caching of external API responses protects against rate limits and improves user experience
- Open-source mapping: MapLibre provides a powerful, cost-free alternative to proprietary map services
- Budget visibility: Showing costs at every level of the hierarchy gives organizers the confidence to plan ambitious trips
In the next article, we shift from the backend to the frontend and explore how Play The Event leverages Angular 19 with Signals and Server-Side Rendering to deliver a fast, responsive, and accessible user interface.







