Skip to content

TripQlub Platform — TeamCity Build & Deployment Strategy

Overview

This document describes the build and deployment pipeline for the TripQlub platform, which consists of a single monorepo containing:

  • Backend API Server — Java/Jetty with multiple modules (Gradle build)
  • Frontend B2C — Public customer-facing web application(s)
  • Frontend Agency/Admin — Internal agency and admin web application
  • Frontend B2B — Business-to-business web application
  • Database Migrations — Versioned SQL scripts managed by Flyway

All applications share a single Git repository. The build strategy uses path-based VCS triggers in TeamCity to ensure that only the affected components are built and deployed when changes are committed.


Repository Structure

tripqlub/
├── backend/
│   ├── api-core/
│   ├── module-bookings/
│   ├── module-itineraries/
│   ├── module-auth/
│   ├── ...
│   └── build.gradle
├── frontend-b2c/
│   ├── src/
│   ├── package.json
│   └── ...
├── frontend-agency/
│   ├── src/
│   ├── package.json
│   └── ...
├── frontend-b2b/
│   ├── src/
│   ├── package.json
│   └── ...
├── db/
│   └── migration/
│       ├── V001__initial_schema.sql
│       ├── V002__add_booking_status.sql
│       └── ...
├── shared/
│   ├── configs/
│   └── ...
├── build.gradle           (root Gradle build)
└── settings.gradle

Note: Adjust directory names to match the actual repository layout. The trigger rules below should be updated accordingly.


Build Configurations

1. Backend — Build & Deploy

Purpose: Compile, test, package, and deploy the Java/Jetty backend API server.

VCS Trigger Rules:

+:backend/**
+:shared/**
+:build.gradle
+:settings.gradle
-:backend/**/*.md

Build Steps:

Step Runner Command / Configuration
1. Build & Test Gradle clean build
2. Package Gradle shadowJar or distTar (depending on packaging strategy)
3. Upload Artifact Command Line Upload JAR/archive to S3 artifact bucket
4. Deploy Command Line Trigger deployment to target environment (see Deployment section)

Gradle Build Step Details:

# Step 1 & 2 can be combined
./gradlew clean build shadowJar \
  -PdbUrl=jdbc:postgresql://localhost:5432/tripqlub_test \
  -PdbUser=%env.DB_USER% \
  -PdbPassword=%env.DB_PASSWORD%

Artifact Paths:

backend/build/libs/*.jar => backend-artifacts

Parameters:

Parameter Description Example
env.DEPLOY_TARGET Target environment dev / stage / prod
env.DB_HOST Database host for the target prod-db.internal
env.BACKEND_HOST Backend server SSH target api.tripqlub.com

2. Frontend B2C — Build & Deploy

Purpose: Build and deploy the public customer-facing web application.

VCS Trigger Rules:

+:frontend-b2c/**
+:shared/**
-:frontend-b2c/**/*.md

Build Steps:

Step Runner Command / Configuration
1. Install Dependencies Command Line cd frontend-b2c && npm ci
2. Build Command Line cd frontend-b2c && npm run build
3. Upload to S3 / Deploy Command Line Sync build output to web server or S3 bucket

Build Commands:

# Step 1: Install
cd frontend-b2c
npm ci

# Step 2: Build with environment-specific config
npm run build -- --env=%env.DEPLOY_TARGET%

# Step 3: Deploy (example: sync to server via rsync over SSH)
rsync -avz --delete dist/ deploy@%env.B2C_HOST%:/var/www/tripqlub-b2c/

Artifact Paths:

frontend-b2c/dist/** => b2c-artifacts

3. Frontend Agency/Admin — Build & Deploy

Purpose: Build and deploy the internal agency and admin web application.

VCS Trigger Rules:

+:frontend-agency/**
+:shared/**
-:frontend-agency/**/*.md

Build Steps:

Step Runner Command / Configuration
1. Install Dependencies Command Line cd frontend-agency && npm ci
2. Build Command Line cd frontend-agency && npm run build
3. Deploy Command Line Sync build output to admin server

Build Commands:

cd frontend-agency
npm ci
npm run build -- --env=%env.DEPLOY_TARGET%
rsync -avz --delete dist/ deploy@%env.AGENCY_HOST%:/var/www/tripqlub-agency/

4. Frontend B2B — Build & Deploy

Purpose: Build and deploy the B2B web application.

VCS Trigger Rules:

+:frontend-b2b/**
+:shared/**
-:frontend-b2b/**/*.md

Build Steps:

Step Runner Command / Configuration
1. Install Dependencies Command Line cd frontend-b2b && npm ci
2. Build Command Line cd frontend-b2b && npm run build
3. Deploy Command Line Sync build output to B2B server

Build Commands:

cd frontend-b2b
npm ci
npm run build -- --env=%env.DEPLOY_TARGET%
rsync -avz --delete dist/ deploy@%env.B2B_HOST%:/var/www/tripqlub-b2b/

5. Database Migrations

Purpose: Execute pending Flyway migrations against the target database.

VCS Trigger Rules:

+:db/migration/**

Build Steps:

Step Runner Command / Configuration
1. Run Flyway Migrate Gradle flywayMigrate with target DB parameters
2. Validate Gradle flywayValidate (optional post-check)

Build Commands:

./gradlew flywayMigrate \
  -Pflyway.url=jdbc:postgresql://%env.DB_HOST%:5432/%env.DB_NAME% \
  -Pflyway.user=%env.DB_USER% \
  -Pflyway.password=%env.DB_PASSWORD% \
  -Pflyway.locations=filesystem:db/migration

Important Considerations:

  • DB migrations should run before backend deployment when both are triggered by the same commit
  • Use a snapshot dependency from the Backend build to the DB Migrations build to enforce ordering
  • For production, consider adding a manual approval step before migration execution

Deployment Strategy

Environment Pipeline

   Commit
 ┌─────────┐     ┌─────────┐     ┌─────────┐
 │   DEV   │────▶│  STAGE  │────▶│  PROD   │
 └─────────┘     └─────────┘     └─────────┘
  (automatic)    (automatic or    (manual
                  manual trigger)  trigger)

Each build configuration should be parameterized by environment. There are two approaches:

Option A: Separate Build Configs Per Environment

Create copies of each build configuration for each environment (e.g., "Backend — Deploy DEV", "Backend — Deploy STAGE", "Backend — Deploy PROD"). The DEV config has VCS triggers; STAGE and PROD are triggered manually or via snapshot dependencies on the DEV build.

Pros: Clear separation, easy to see what's deployed where, simple permissions (restrict PROD configs).

Cons: More configurations to maintain.

Option B: Single Build Config with Environment Parameter

Each build configuration accepts a DEPLOY_TARGET parameter. The VCS trigger deploys to DEV automatically. STAGE and PROD deployments are triggered manually with the parameter overridden.

Pros: Less duplication, fewer configurations.

Cons: Harder to audit what's deployed where.

Recommendation: Option A is better for your setup — it's more explicit and allows you to set different permissions for production deployments.


Dependency & Ordering

Snapshot Dependencies

For cases where a single commit triggers multiple builds (e.g., changes to shared/ trigger all builds), configure snapshot dependencies to control execution order:

DB Migrations
  Backend
     ├──▶ Frontend B2C
     ├──▶ Frontend Agency
     └──▶ Frontend B2B

This ensures: 1. Database migrations run first 2. Backend builds and deploys after migrations succeed 3. Frontend builds run in parallel after backend succeeds (if there are frontend changes)

Note: Snapshot dependencies enforce ordering only when builds are triggered together. If only a frontend changes (and backend/DB are not triggered), the frontend builds independently without waiting.


TeamCity VCS Trigger Configuration (UI Steps)

For each build configuration:

  1. Navigate to Build Configuration SettingsTriggers
  2. Click Add new triggerVCS Trigger
  3. Expand Show advanced options
  4. In the VCS trigger rules field, enter the path rules listed above for the specific configuration
  5. Set Quiet period to 30–60 seconds (batches rapid commits into a single build)
  6. Under Branch filter, set to your main/deploy branch:
    +:<default>
    +:refs/heads/main
    

Checkout Rules (Optional Optimization)

If you want to reduce checkout time, you can add VCS Checkout Rules to only fetch relevant directories. However, since your builds may need cross-directory access (e.g., shared configs), it's generally safer to check out the full repository and rely only on trigger rules for filtering.


Full Deployment Sequence Example

Scenario: A commit modifies backend/ and db/migration/.

  1. TeamCity detects the commit and evaluates trigger rules
  2. DB Migrations build triggers (matches db/migration/**)
  3. Backend build triggers (matches backend/**)
  4. Snapshot dependency ensures DB Migrations completes first
  5. DB Migrations runs flywayMigrate — applies pending scripts
  6. Backend builds, tests, packages, and deploys the new JAR
  7. Frontend builds are not triggered (no matching path changes)

Scenario: A commit modifies only frontend-b2c/.

  1. TeamCity detects the commit
  2. Only Frontend B2C build triggers
  3. B2C app is built and deployed
  4. Backend, other frontends, and DB migrations are untouched

Scenario: A commit modifies shared/.

  1. All build configurations trigger (all include shared/** in their rules)
  2. Dependency chain ensures correct ordering
  3. Everything is rebuilt and redeployed

Gradle Configuration for Flyway

Add to your root build.gradle:

plugins {
    id 'org.flywaydb.flyway' version '10.21.0'
}

flyway {
    url = project.findProperty('flyway.url') ?: 'jdbc:postgresql://localhost:5432/tripqlub'
    user = project.findProperty('flyway.user') ?: 'postgres'
    password = project.findProperty('flyway.password') ?: ''
    locations = [
        project.findProperty('flyway.locations') ?: 'filesystem:db/migration'
    ]
    baselineOnMigrate = true
    baselineVersion = '0'
}

Baselining an Existing Database

For your existing databases that already have a schema:

# Run once per existing database to mark current state as the baseline
./gradlew flywayBaseline \
  -Pflyway.url=jdbc:postgresql://prod-host:5432/tripqlub \
  -Pflyway.user=admin \
  -Pflyway.password=*** \
  -Pflyway.baselineVersion=1

This creates the flyway_schema_history table and marks version 1 as already applied. All future migrations (V2, V3, ...) will be applied normally.


Migration Script Naming Convention

db/migration/
├── V001__initial_baseline.sql
├── V002__add_booking_status_column.sql
├── V003__create_itinerary_templates_table.sql
├── V004__add_agency_commission_fields.sql
└── ...

Rules: - Prefix: V followed by a zero-padded version number - Double underscore __ separates version from description - Description uses underscores for spaces - Once applied to any environment, never modify an existing migration — always create a new one - For reversible changes, consider also creating U (undo) scripts: U003__drop_itinerary_templates_table.sql


Security Notes

  • Store database credentials as TeamCity password parameters (type: Password), never in plain text
  • Use environment-specific parameter sets to prevent accidental production deployments
  • For production deployments, enable manual approval in the build chain
  • The deployment pipeline should use the air-gapped S3/SQS mechanism already in place for production environments