A Spring Boot microservice for managing CRM WORKFLOW, backed by a PostgreSQL database with schema managed by Liquibase.
📄 New here? For a business & solution overview of what this service does and why, see docs/BUSINESS_MODEL.md.
- Java 21
- Spring Boot 3.5.15 (Web, Data JPA, Validation, Security, OAuth2 Resource Server, OAuth2 Client)
- Spring Cloud OpenFeign 2025.0.3 (declarative HTTP client for outbound calls to crm-account)
- PostgreSQL (
postgresqlJDBC driver) - Liquibase (schema migrations)
- MapStruct 1.5.5 (entity/DTO mapping)
- Lombok
- Maven
- Java 21+
- Maven 3.9+ (or use the included Maven Wrapper)
- Docker & Docker Compose (for the PostgreSQL database)
Connection and server settings live in src/main/resources/application.yaml.
Defaults:
| Setting | Default |
|---|---|
| Server port | 8401 |
| Datasource URL | jdbc:postgresql://localhost:5432/crm |
| Username | crm_workflow |
| Schema | crm_workflow |
Liquibase runs on startup using classpath:db/changelog/master.xml (context
dev) to create the WORKFLOW tables, sequences, and indexes. JPA ddl-auto is
none — the schema is owned entirely by Liquibase. The changelog is currently
empty; add changesets as the domain model lands.
A docker-compose.yml at the project root starts a PostgreSQL 16 instance
(container postgres_db) with a superuser admin / admin and a crm
database, exposed on port 5432:
docker compose up -dThe compose file only creates the admin superuser and crm database. The
application connects as a dedicated crm_workflow role that owns its own schema,
so after the container is up, create the role and schema once:
CREATE USER crm_workflow
WITH PASSWORD 'admin';
CREATE SCHEMA crm_workflow
AUTHORIZATION crm_workflow;
Run them against the crm database.
With the role and schema in place, Liquibase can create its tracking tables and apply the changelog on the next application start.
# Run the app
./mvnw spring-boot:run
# Build a jar
./mvnw clean package
# Run tests
./mvnw testThe service starts on http://localhost:8401.
A Dockerfile is provided that packages the built JAR on top of an
eclipse-temurin:21-jre-alpine base image. Build the JAR first, then the image:
./mvnw clean package
docker build -t crm-workflow .Run the container, mapping the service's port. Note the Dockerfile currently
exposes 8401 while the app listens on 8401, so either update EXPOSE or
override the port at run time:
docker run --rm -p 8401:8401 crm-workflowThis service is a pure OAuth2 resource server — it validates bearer JWTs issued by a Keycloak realm on every request. There's no login flow or session state here; Keycloak owns authentication, this service only enforces it.
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: http://localhost:8180/realms/crm-realmSpring uses that issuer to auto-discover Keycloak's JWKS and verify each
token's signature, expiry, and issuer. Only /api/** is locked down
(SecurityConfig); everything else is permitAll(). Sessions are stateless
and CSRF is disabled — bearer tokens don't need either.
Keycloak ships roles under realm_access.roles, not the scope claim Spring
expects by default, and hands you a raw Jwt rather than a domain object. Two
small converters close that gap:
| Class | Job |
|---|---|
KeycloakRealmRoleConverter |
maps realm_access.roles → ROLE_* authorities |
UserPrincipalJwtConverter |
builds a typed UserPrincipal (userId, username, email, name, department, phone, mobile) as the authentication's principal |
Anywhere in the codebase, SecurityUtil.getUserPrincipal().
Role names live as constants in SecurityRoles and are enforced per endpoint
via @PreAuthorize:
| Role | Can do |
|---|---|
ORG_ADMIN |
Create, view, and activate workflow definitions |
SALES_REP |
Create and submit workflow requests |
SALES_MANAGER |
Decide (approve/reject) a request step |
| any authenticated user | View existing workflow requests |
A Jenkinsfile defines a declarative pipeline that:
- Build — runs
mvn clean package -DskipTestsand archives the resulting JAR. - Test — runs
mvn testand publishes the JUnit surefire reports. - Docker Build & Push — logs in to Docker Hub, builds the image tagged with
the Jenkins
BUILD_NUMBER, and pushes it, then removes the local image afterward.
The pipeline requires JDK 21 (jdk-21) and Maven (maven-3.9) tool
installations configured in Jenkins, plus a docker-hub-credentials
username/password credential. The DOCKER_HUB_USER build parameter defaults to
mba90 — update it to your own Docker Hub account. The pipeline still uses
crm-workflow for IMAGE_NAME and the artifact pattern
target/crm-workflow-*.jar, which does not match this project's artifactId
(crm-workflow); update both in the Jenkinsfile.