Skip to content

Architecture

Platform

  • Mendix 10.24.21 (LTS), split-MPR format v2.
  • WildernessWindow.mpr is a SQLite database containing only the Unit table - the document tree (UnitID, ContainerID, ContainmentName, ContentsHash). Document content lives in mprcontents/<xx>/<yy>/<guid>.mxunit binary blobs, one per document. Practical effect: git diffs are per-document, so parallel work merges far better than the old single-file .mpr, but nothing in the model is greppable without a decoder (see tools/mxinspect.py).
  • Java layer: 872 .java files across 50 packages, 551 Java actions. pom.xml exists only for IDE support and still declares source/target 1.8 while Mendix 10 runs on Java 21 - harmless for the runtime, misleading for IDE users.
  • Front end: Atlas Core / Atlas Web Content plus 71 widget packages, mixing current Mendix widgets (Datagrid 2, Gallery, Combobox) with older third-party and one-off widgets (jQueryUIDateTimePicker, InterActiveGrid, mobino.RichTextCustome, nagarro.WrittingAssistant, wilderness destinations.StringHtml).

Module layout

108 modules; the full table is inventory/module-inventory.md. Functionally they group like this:

Group Modules
Core selling Booking, BookingTemplates, BookingGuestManagement, Pricing, PricingDynamic, Availability, XtremeAvailability, LiveRequest
Master data BookingMasterData (RARA), Configuration, Common, SetupWizard
Customers / CRM GuestManagement, GuestManagementAPI, Agency, AgentOnBoarding, Enquiries, HubSpot, HubSpot_DMC, WildernessLeadModel, Media, DonorDatabase, Windfall
Products Itinerary, Itinerary_SafariPortal, Touring, Explorations, Transfers, Interconnect, WAir, TravelStart, Prepurchases, Exo, PanStrat, Wedge
Money FinancePortal, TourplanInvoicing, PaymentGateway, StaffRemuneration
Operations LodgeManagement, IncidentManagement, Maintenance, EAH, SRM, Supplier_Portal
Portals / content AgentPortal, InformationCentre, Training, Awards, Anonymous, Layouts
Integration API, API_V3, DataManagement, WishAPI, hostConnect, HTI, HConnect, SiteConnect, Siteminder, Nightsbridge, NightsBridge_V5, Profitroom, ResRequest, Mautourco, SchedwinAPI, Onbase, Jotform, Interconnect, History
Platform / support Administration, Logging, Tools, Dashboard, WildernessScheduler, Encryption, Ldap, GoogleAnalytics, OpenAIConnector, EnumEntityBuilder
Marketplace (28) Atlas_Core, Atlas_Web_Content, CommunityCommons, DataWidgets, DocumentGeneration, Email_Connector, ExcelImporter, ExternalDatabaseConnector, GenAICommons, MxModelReflection, NanoflowCommons, OQL, PDFUtils, ProcessQueue, TaskQueueManager, WebActions, WordTemplate, XLSReport, Calendar, Countries, Encryption, EnumToList, FileDropper, ImageCrop, Ldap, LocalFileOperations, OpenAIConnector, RedirectURLUsingJs

Module boundaries are not enforced barriers: Booking alone holds 2,553 microflows and 496 pages, and cross-module calls are common (Pricing.Sub_PriceBookingAll has 89 callers spread across modules).

Pricing and PricingDynamic are documented in depth, and the shape is worth knowing at architecture level: there is no rule engine. Pricing.PricingRule is a 54-attribute entity with 23 specialisations, qualification is a pipeline of list filters, and application is a type switch — so pricing behaviour is configuration data, not model logic. Detail in pricing-rules.md, pricing-calculation.md and pricing-manual-adjustments.md.

Data model

2,773 entities, 96 domain models. Biggest:

Module Entities
BookingMasterData 176
Booking 147
DataManagement 135
API 132
Siteminder 115
HTI 114
SiteConnect 110
GuestManagement 95

Note the shape: integration modules carry very large domain models because each channel partner's payload is mirrored into its own entities before being mapped onto core booking entities. Core booking objects are Booking -> TravelDay / TravelLocation -> BookingLine -> DetailedBookingLine, plus Referral, BookingCorrespondence, TourplanBookingServiceLine, TPItinerary.

Full entity lists: inventory/data-model.md, inventory/entities.json.

Runtime configuration

  • Settings$ProjectSettings holds 17 server configurations. Default and Configuration are HSQLDB placeholders; the other 15 are individual developers' local SQL Server setups (Joeg, Joeg_Dev, Joeg_Test, Ashley D, Ashley T, AllistairSquirrel, Allistair2ndWork, Matt_Squirrel, faiz, Rion_Lyan, Carl - Home, Stanley-lion, Altostratus, Local, WINDOWSSERV-001). Several carry plaintext database passwords - see 05-conventions-and-risks.md.
  • Most configurations set -Dhttps.proxyHost=172.22.0.17 -Dhttps.proxyPort=8080, so outbound integration traffic goes through a corporate proxy.
  • Environment awareness is in the model, not just config: Administration exposes LocalEnvironment / TestEnvironment / AcceptanceEnvironment / ProductionEnvironment constants, Administration.GetServerSettings (466 callers) and GetEnvironment (118 callers), plus Administration/DetermineServer flows. Flows use these to suppress real emails and integration calls off production (AdvEmail.DevelopmentMailRedirect).
  • 108 constants, heavily used for routing addresses (AdvEmail.*Email), retries and retention.

Security model

Setting Value
Security level CheckEverything (entity + page + microflow security enforced)
Demo users disabled
Guest access enabled, guest role Anonymous_UseWithCaution
Strict mode / strict page URL check off
Password policy min length 8, digit + mixed case + symbol required
Admin account MxAdmin, role Debug
User roles 66

66 user roles is large; roles are fine-grained by function and by portal (InfoPortalRates, InfoPortalAP, AfterHours, AfterHoursRead, FinanceReadOnly, RaraSupplier, SupplierUser, MediaMember, SchedwinApiUser, GuestManagementApiUser, SiteConnectUser...). Anonymous access is genuinely used - the Anonymous module serves supplier deep links, uploads and issue reporting without login.

Batch processing

Two layers, and the important part is that the job list is data, not model:

  1. Mendix scheduled events (31) - declared in the model (inventory/scheduled-jobs.md). Maintenance owns 16 of them, on fixed cadences: SE_Every30Seconds, SE_Every1MinuteOther, SE_Every1MinuteDailyJobs, SE_Every2Minute, SE_Every10Minute, SE_Every30Minute, SE_EveryHour, SE_EveryDay, SE_EndOfDayProcesses, plus email fetch/send and SE_WishHistory.

But only 2 of the 31 are enabled and included in the model - both GenAICommons cleanup jobs. Every Maintenance.SE_* job is marked Enabled: false and/or Excluded from deployment. On Mendix Cloud, scheduled events are switched on per environment, so the model tells you a job exists and its interval, not whether it runs. To know the live schedule you must look at the environment's model options in the Mendix Cloud portal (or the runtime's scheduled-event settings). 2. A home-grown process queue - Maintenance.Every30Seconds_QueueBuilder -> BuildProcessQueue -> AddProcessesToQueueConfig, and Every30Seconds_QueueRunner -> RunProcessQueue (with RunProcessQueue_Backup as a second runner). Which processes run, and in what order, comes from queue configuration rows in the database. Reading the model tells you the scheduler exists but not what it schedules; you need a database query or the in-app admin screens for that.

Alongside it: the Marketplace ProcessQueue module (with SE_ReinitializeQueues, SE_CleanupFinishedQueuedActions), TaskQueueManager, and 5 Mendix task queues (Hubspot_Task_Queue, LogPricingQueue, RARA_Task_Queue, GMAPI_TaskQueue, API_TaskQueue).

  1. Fire-and-forget background microflowsCommunityCommons.executeMicroflowInBackground at 49 call sites, running 17 distinct targets in their own transaction after the caller commits. The return value is checked at none of them, so failures are silent. Onbase.BookingStatus_Post accounts for 27 of the 49; the rest include outbound partner webhooks, HubSpot deal updates, a NightsBridge channel booking, and — in one path — a booking confirmation. Inventory: reports/async-handoffs.md.

Four overlapping asynchronous mechanisms — scheduled events, the home-grown process queue, Mendix task queues and fire-and-forget background calls — is itself a finding. They differ in ways that matter operationally: queued work is retried and observable, background work is neither, and neither appears in a synchronous call graph. Choosing between them currently looks like a matter of habit rather than of policy.

Logging and observability

  • Logging.LogMessage is the single most-called microflow in the app (823 callers), with Tools.LogMicroflow (349) and Booking.LogEvent (200) close behind. Logging is applied by convention, in-model, rather than via platform log nodes only.
  • WishAPI.LogWishMessagesToDisk (92 callers) and API.LogXMLRequestResponse (45) persist integration payloads for dispute resolution.
  • Tools.UptimeMonitor is a published REST service used for external health checking, with a dedicated HealthCheck user role.
  • One developer configuration references New Relic (-Dnewrelic.environment=), so APM exists at least in some environments.