A sample Spring-based application
Find a file
2026-02-08 21:36:09 +05:30
.devcontainer Add a Dockerfile for dev environments other than codespaces 2024-11-28 14:45:59 +00:00
.github Support building with Java 17 2025-11-26 12:49:50 +00:00
.mvn/wrapper Upgrade to Apache Maven 3.9.12 2025-12-30 09:41:36 +01:00
gradle/wrapper Upgrade to Spring Boot 4.0.0 2025-11-25 08:46:39 +00:00
k8s Upgrade to Spring Boot 4.0.0 2025-11-25 08:46:39 +00:00
src updates comments 2026-02-08 21:36:09 +05:30
.editorconfig Add Gradle files indentation to .editorconfig 2024-02-20 17:14:43 +00:00
.gitattributes Update to current versions 2025-10-06 19:23:19 +01:00
.gitignore Switch to building the project with Java 25 2025-10-14 12:37:18 +02:00
.gitpod.yml Add devcontainer and gitpod 2022-06-09 11:24:17 +01:00
build.gradle Upgrade to native buildtools 0.11.3 2025-12-30 09:41:36 +01:00
docker-compose.yml added Feature Flag 2026-02-08 12:53:29 +05:30
gradlew Upgrade to Gradle 9.1.0 2025-10-14 12:36:02 +02:00
gradlew.bat Upgrade to Gradle 9.1.0 2025-10-14 12:36:02 +02:00
LICENSE.txt Add license file 2021-10-05 16:49:36 +01:00
mvnw Upgrade to Spring Boot 4.0.0 2025-11-25 08:46:39 +00:00
mvnw.cmd Update to current versions 2025-10-06 19:23:19 +01:00
pom.xml added Feature Flag 2026-02-08 12:53:29 +05:30
README.md updated Readme file 2026-02-08 20:04:05 +05:30
settings.gradle Make build work with Gradle 2021-12-16 11:25:09 +00:00

Feature Flag Enabled Spring PetClinic

This project is an extension of the official Spring PetClinic application with a custom-built Feature Flag service implemented from scratch (without using libraries like FF4J or Togglz).

The feature flag system allows enabling/disabling application features dynamically using database-driven flags that persist across restarts.


How to Run the Application

Prerequisites

  • Java 17+
  • Maven 3.8+
  • Docker & Docker Compose

Steps

  1. Clone the repository

    git clone https://github.com/XTiNCT-7/spring-petclinic.git
    cd spring-petclinic
    
  2. Start MySQL using Docker

    docker-compose up mysql
    
  3. Build and run the application

    mvn clean spring-boot:run
    
  4. Access the application:

    • Application UI: http://localhost:8080
    • Feature Flag APIs: http://localhost:8080/feature-flags

Assumptions & Design Decisions

  • Feature flags are stored in the database and persist across application restarts.
  • No authentication is applied to feature flag management APIs (as per requirement).
  • Feature flag evaluation is centralized in a helper service so it can be reused across controllers, services, and views.
  • A custom annotation (@FeatureToggle) is used to guard controller endpoints.
  • Thymeleaf views are conditionally rendered using model attributes derived from feature flag checks.
  • Feature flag behavior supports more than boolean enable/disable:
    • Global enable/disable
    • Whitelist-based access
    • Blacklist-based restriction
    • Percentage rollout (future-safe design)

Feature Flags Implemented

Feature Flag Key Type Controls Implementation Location
ADD_NEW_PET SIMPLE Enables adding a new pet to an owner PetController, ownerDetails.html
ADD_VISIT SIMPLE Enables adding a visit for a pet VisitController, ownerDetails.html
OWNER_SEARCH SIMPLE Enables owner search functionality OwnerController, findOwners.html

Example

  • If ADD_NEW_PET is disabled:
    • The "Add New Pet" button is hidden in the UI
    • Direct access to /owners/{id}/pets/new is blocked using @FeatureToggle

Feature Flag Management APIs

Base Path: /feature-flags

Create Feature Flag

POST /feature-flags

Request Body

{
  "flagKey": "ADD_NEW_PET",
  "description": "Controls whether users can add new pets",
  "enabled": true,
  "flagType": "SIMPLE"
}

Get All Feature Flags

GET /feature-flags

Get Feature Flag by Key

GET /feature-flags/{flagKey}

Update Feature Flag

PUT /feature-flags/{id}

Delete Feature Flag

DELETE /feature-flags/{id}

Custom Annotation Usage

@FeatureToggle(
    key = "OWNER_SEARCH",
    disabledMessage = "Owner search is restricted",
    disabledRedirect = "/owners/find"
)

This annotation prevents access to the controller method when the feature is disabled and optionally redirects the user.


References