PORTFOLIO SERVICE

The backend engine powering my personal portfolio web application. Built with Java 17 and Spring Boot 3.5.5, this service exposes secure REST endpoints for managing my professional skills, projects, education and experiences.

Frontend Reference: Looking for the UI client? Check out the portfolio-ui repository.

Architecture & Tech Stack

Architectural Decision Records (ADR)

ADR 1: PostgreSQL over MySQL

ADR 2: Client-Generated Entity Keys (my_key via UUID)

ADR 3: Data Transfer Objects (DTO) Isolation via Java Records

ADR 4: Stateless Authentication & Targeted CSRF Disabling

ADR 5: OpenAPI & Swagger UI for Development

Code Conventions & Design Patterns

Entity Lifecycle Hooks (@PrePersist and @PreUpdate)

To ensure auditing fields remain clean and automated, entities utilize JPA lifestyle annotations rather than manual controller/service-level date mapping.

Standardized Domain Handling

Database Migration Setup

Liquibase handles all schema initializations and schema upgrades prior to application runtime bootstrap.

Master Changelog (src/main/resources/db/changelog/changelog-master.xml)

<?xml version="1.0" encoding="UTF-8"?> <databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd"> <include file="db/changelog/changelog-v1.0.sql" /> </databaseChangeLog>

NOTE: The referenced .sql changelog files must contain precise Liquibase SQL format headers (e.g., --liquibase formatted sql) followed by distinct changeSet IDs (--changeset author:id), otherwise the migration parser will crash during compilation.

Logback Configuration

The system records console behaviors alongside rolling physical files partitioned on a monthly timeline.

<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/portfolio_service.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/portfolio_service.%d{yyyy-MM}.log.gz</fileNamePattern> <maxHistory>12</maxHistory> </rollingPolicy> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> </root> </configuration>

CI/CD Flow: From Local Dev to Production

Pipeline Overview

When a commit lands on the render branch, the automated GitHub Actions workflow kicks off:

Local Compilation

To compile the package locally (bypassing unit validations if necessary):

mvn clean package -DskipTests

Production Resource Profile (application-prod.yaml)

Configured to seamlessly bind with the hosting infrastructure environment variables:

server: port: ${PORT:8080} address: 0.0.0.0 spring: datasource: url: jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME} username: ${DB_USER} password: ${DB_PASSWORD} security: oauth2: resourceserver: jwt: jwk-set-uri: https://auth-server.com/.well-known/jwks.json management: endpoints: web: exposure: include: health endpoint: health: show-details: never #Keeps it lightweight and secure for production

NOTE: Make sure DB_HOST, DB_PORT, DB_NAME, DB_USER, and DB_PASSWORD are configured securely within Render’s dashboard environment variables.

Dockerization (Dockerfile)

FROM openjdk:25-ea-26-jdk-oraclelinux8 ADD target/portfolio-service.jar portfolio-service.jar ENTRYPOINT ["java", "-jar", "/portfolio-service.jar"]

Automated Pipeline Blueprint (.github/workflows/deploy.yml)

name: Build, Push, Deploy on: push: branches: - render jobs: build-deploy: runs-on: ubuntu-latest steps: - name: Checkout portfolio-service uses: actions/checkout@v3 - name: Create Maven settings.xml run: | mkdir -p ~/.m2 cat > ~/.m2/settings.xml <<EOF <settings> <servers> <server> <id>github</id> <username>Kranthi0307</username> <password>${{ secrets.SHARED_SERVICE }}</password> </server> </servers> </settings> EOF - name: Build portfolio-service run: mvn clean package -DskipTests - name: Log in to Docker Hub uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_ACCESS_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - name: Build and push Docker image uses: docker/build-push-action@v4 with: context: . push: true tags: | docker.io/${{ secrets.DOCKER_USERNAME }}/portfolio-service:latest docker.io/${{ secrets.DOCKER_USERNAME }}/portfolio-service:${{ github.sha }} - name: Deploy to Render run: | curl -X POST \ -H "Accept: application/json" \ -H "Authorization: Bearer ${{ secrets.RENDER_API_KEY }}" \ -H "Content-Type: application/json" \ -d '{}' \ https://api.render.com/v1/services/${{ secrets.RENDER_SERVICE_ID }}/deploys

Troubleshooting

Future Enhancements