Skip to content

Livekus/simpleFullstackAngularJavaSpringBoots

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

5 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Feature Flag Admin — Angular 16 + Spring Boot 3

Minimal, production-grade sample:

  • Angular 16 SPA: CRUD, optimistic updates, CDK virtual scroll, reactive forms, standalone components, strict TS, Jest unit tests, Playwright E2E.
  • Spring Boot 3 API: layered architecture, pagination/search, cache hints (ETag/Cache-Control), Bean Validation, RFC7807 Problem Details, Flyway, Testcontainers.

Assumptions (edit as needed): No authentication in the sample; audit user is provided via X-Actor header. Default timezone is UTC everywhere.


Table of Contents


Repo layout

feature-flag-admin/
  README.md
  docker-compose.yml
  backend/
    pom.xml
    src/main/resources/
      application.yml
      application-dev.yml
    db/migration/
      V1__init.sql
  frontend/
    package.json
    angular.json
    src/...
  .github/workflows/ci.yml

Prerequisites

  • Java 21+, Node 20+, Docker (Compose v2).
  • DB is PostgreSQL 16; system timezone UTC.

Quickstart

Option A — Docker Compose

docker compose up --build
# SPA http://localhost:4200 (nginx)
# API http://localhost:8080
# DB  localhost:5432  (user/pass: ffa/ffa, db: ffa)

Option B — Hot-reload dev

Terminal A (DB)

docker compose up db

Terminal B (API)

cd backend
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev

Terminal C (SPA)

cd frontend
pnpm install
pnpm start  # http://localhost:4300 (dev server, proxy to backend)

Backend (Spring Boot 3)

Build & run

cd backend
./mvnw -B -ntp verify
./mvnw spring-boot:run -Dspring-boot.run.profiles=dev

Configuration

backend/src/main/resources/application.yml (trimmed):

spring:
  application.name: feature-flag-admin
  datasource:
    url: jdbc:postgresql://localhost:5432/ffa
    username: ffa
    password: ffa
  jpa:
    hibernate.ddl-auto: validate
    properties:
      hibernate.jdbc.time_zone: UTC
      hibernate.format_sql: true
    open-in-view: false
  flyway.enabled: true
server:
  port: 8080
management.endpoints.web.exposure.include: health,info,metrics

Dev overrides:

# backend/src/main/resources/application-dev.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/ffa
  jpa:
    show-sql: true
logging.level.org.hibernate.SQL: debug

Database migrations

V1__init.sql creates f_flag and audit_log tables (PK/UK on key, @Version column, audit JSON, indexes).

API surface (OpenAPI)

  • Swagger UI: http://localhost:8080/swagger-ui/index.html (when Swagger deps included)
  • Base path: /api/v1

Entities (conceptual)

{
  "Flag": {
    "id": "UUID",
    "key": "string",         // unique, kebab/snake-case
    "name": "string",
    "description": "string?",
    "enabled": true,
    "version": 3,            // JPA @Version, also exposed via ETag
    "createdAt": "2024-01-01T00:00:00Z",
    "updatedAt": "2024-01-02T00:00:00Z"
  }
}

Endpoints

  • GET /flags?page=0&size=20&sort=key,asc&q=searchTerm — paged list (ILIKE on key/name/desc)
  • GET /flags/{id} — returns 200 with ETag: "v{version}" or 404
  • POST /flags — create (requires header X-Actor)
  • PUT /flags/{id} — update (requires If-Match: "v{version}" for concurrency)
  • PATCH /flags/{id} — partial update (optional)
  • DELETE /flags/{id} — delete (optimistic locking respected)

Caching/Concurrency

  • ShallowEtagHeaderFilter adds ETag; clients send If-None-Match / If-Match.
  • GET responses include Cache-Control: private, max-age=0, must-revalidate.

Problem Details (errors)

  • RFC7807 with fields: type, title, status, detail, instance, plus violations[] for Bean Validation.

Happy path smoke (PowerShell)

$api='http://localhost:8080/api/v1/flags'
$h=@{'Content-Type'='application/json';'X-Actor'='demo-user'}
$body=@{key="beta_ui_$(Get-Random)";name="Beta UI";enabled=$true}|ConvertTo-Json
$created = irm -Method Post -Uri $api -Headers $h -Body $body
irm ($api + '/' + $created.id) -Headers $h

Testing

  • Unit + slice tests: ./mvnw -B -ntp test
  • Integration tests: Testcontainers (PostgreSQL) via *IT.java with Failsafe.
  • FlagControllerTest covers 200/404, ETags, paging.

Key dependencies (excerpt):

  • Spring Boot: Web, Validation, Data JPA, Actuator, Cache
  • DB/tooling: PostgreSQL, Flyway
  • Mapping: MapStruct (optional)
  • Test: Spring Boot Starter Test, Testcontainers (JUnit 5)

Frontend (Angular 16)

Install & dev

cd frontend
pnpm install
pnpm start   # http://localhost:4300 (proxy -> http://localhost:8080)

Build

pnm build   # dist/ output

Unit tests (Jest)

pnpm test

Notes: jest.config.cjs, setup-jest.ts included. If adjusting structure, update transformIgnorePatterns and setup path accordingly.

E2E (Playwright)

One-shot orchestration from repo root (starts API, waits for health, then runs E2E):

.\e2e.ps1

The script uses mvnw to boot the API, polls /actuator/health, then runs pnpm dlx playwright test.

Frontend features

  • Standalone components, strict TS, RxJS best practices.
  • Reactive forms (validation), HTTP interceptors for ETag/X-Actor, route guards.
  • CDK Virtual Scroll for lists, optimistic UI updates with server reconciliation.
  • Lightweight state services (or NgRx if scaling up).

CI (GitHub Actions)

Backend job (Temurin 21)

- uses: actions/setup-java@v4
  with: { distribution: temurin, java-version: '21' }
- run: ./mvnw -B -ntp verify
  working-directory: backend

Frontend job (Node + pnpm)

- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: pnpm
- uses: pnpm/action-setup@v4
  with: { version: 10 }
- run: pnpm install
  working-directory: frontend
- run: pnpm test && pnpm build
  working-directory: frontend

Use the wrapper (mvnw) to pin Maven in CI; the pnpm action avoids “pnpm: command not found”.


Troubleshooting

  • Flyway checksum mismatch (dev only)
    ./mvnw -q -Dflyway.clean-disabled=false flyway:repair or revert the migration.

  • PowerShell ****************************Invalid URI
    Ensure $api has no trailing slash; build URLs via ($api + '/' + $id).

  • Angular/Jest ESM issues
    Keep jest.config.cjs; adjust transformIgnorePatterns; ensure setup-jest.ts path matches.

  • Docker on Windows
    If port 4200 is busy, change docker-compose.yml mapping or stop local Angular dev server.


Notes & trade-offs

  • Concurrency: Optimistic UI + JPA @Version provides end-to-end conflict detection.
  • Caching: ShallowEtagHeaderFilter + Cache-Control for GET; @Cacheable for get(id) (optional).
  • Search: Simple q against key/name/desc (ILIKE). Extend to advanced filters as needed.
  • Lombok: Speeds POJOs; be aware of IDE plugin and potential noise in stack traces.
  • Mapping: MapStruct gives fast, explicit mappings; manual mapping keeps deps minimal.

License

MIT License

Copyright (c) 2025 Feature Flag Admin Contributors

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published