← back to clients
banking · ongoing

raiffeisen — commissions

client
Raiffeisenbank Russia
industry
Banking / Financial services
role
Dedicated embedded fullstack engineer
timeline
May 2025 – present, ongoing
team
1 fullstack engineer
status
ongoing · ongoing

Raiffeisen — commissions — Case Study

Commissions management surface for Raiffeisenbank Russia's retail saving-products. React + Vite frontend paired with a Kotlin/Spring Boot service on Java 21, deployed via Helm to the bank's Kubernetes cluster.

Summary

commissions is the commissions workflow for Raiffeisenbank Russia's retail saving-products — the screens bank operators use to define and review commission rules, generate Excel reports, and the Kotlin service that owns the data, the IBM MQ integrations, and the OpenAPI contract. The frontend (commissions-ui, internal name rcsm-ui) and backend (rams-commission) share a single OpenAPI spec and regenerate types from it on both sides. topsweteam's embedded engineer is the active fullstack contributor.

  • Client: Raiffeisenbank Russia
  • Engagement: 1 dedicated embedded fullstack engineer (topsweteam)
  • Status: Live in production, ongoing
  • Industry: Banking / Financial services

Challenge

Commissions for retail saving-products touch a slow-moving compliance domain: every rule change has to be auditable, every report has to match what other downstream systems already report, and the integrations are with the bank's existing enterprise message bus rather than something modern. The product needs an operator-friendly UI that doesn't drift from the backend's data model, and a backend that talks the same protocols the bank's older systems expect — IBM MQ rather than Kafka, Excel rather than CSV, an OpenAPI contract rather than ad-hoc JSON.

For a one-engineer fullstack engagement, the constraint is keeping the two halves in lockstep without separate frontend and backend specialists negotiating a contract over Slack.

Approach

The engagement is fullstack-by-design. Same engineer owns both repositories, both deployments, both code reviews. The discipline that keeps this safe is the OpenAPI contract.

  • OpenAPI as the single source of truth. rams-commission owns openapi_spec.yaml. The backend regenerates server stubs from it via the Gradle openapi.generator plugin. The frontend regenerates a TypeScript Fetch client via openapi-generator-cli (npm run generate_api). Schema drift is impossible — both sides recompile against the same spec.
  • Kotlin + Spring Boot for the service. Java 21 toolchain, Spring Boot starters for web, JPA, validation, security, OAuth2 resource server. The bank's Raiffeisen Artifactory is the dependency mirror; mavenCentral is unreachable from the build. libs.versions.toml gives us deterministic dependency resolution.
  • PostgreSQL with Liquibase for migrations. preliquibase for environment-specific bootstrap, then ordinary Liquibase changelogs for schema evolution. Both run on app start so a fresh environment is always one docker compose up away.
  • IBM MQ for messaging. The bank's enterprise messaging stack is IBM MQ (Jakarta JMS client + spring-jms). We don't fight it — we integrate.
  • Apache POI for Excel exports. Operators want .xlsx, not CSV. POI is the reliable, correct answer.
  • React 18 + Redux Toolkit + react-hook-form + Zod for the UI. Redux Toolkit because the operator workflows have enough cross-screen state to justify it; react-hook-form + Zod for the rule-editing forms (they're complex, with conditional validation). @fcc/ui for everything visual.
  • OIDC for identity on both sides. react-oidc-context on the frontend; Spring Security OAuth2 Resource Server on the backend, validating tokens against the bank's IdP.

Solution

Two repositories, one product. The Kotlin service is a standard Spring Boot 3 application: REST controllers generated from the OpenAPI spec, JPA entities and repositories over PostgreSQL, Liquibase migrations, IBM MQ listeners and publishers for the messaging integrations, Apache POI for the Excel export endpoints, Springdoc serving the live API docs, Micrometer publishing Prometheus metrics. The frontend is a Vite/React/TypeScript app rendering against @fcc/ui, with Redux Toolkit slices for the operator state, react-hook-form + Zod for the editing forms, and a generated TypeScript Fetch client that strictly mirrors the backend.

Both halves deploy to the bank's Kubernetes cluster via Helm — the frontend has the same application + proxy chart pair the rest of the platform uses; the backend deploys as a standard Spring Boot service. CI is GitLab.

Architecture

A single OpenAPI spec lives in the backend repository. The backend's Gradle build runs the OpenAPI generator on every build to regenerate Spring controllers and DTOs. The frontend's npm run generate_api script regenerates a TypeScript Fetch client into src/API/rcsm. The frontend imports the generated client directly; UI code never hand-writes request bodies or response types. When the spec changes, both sides recompile and any drift surfaces as a TypeScript or Kotlin compile error.

The backend is a layered Spring Boot service: controllers (generated) → application services → JPA repositories → PostgreSQL. IBM MQ listeners run as Spring components subscribing to the bank's queues; the same configuration also publishes outbound messages. Excel exports are streaming POI workbooks served as application/vnd.openxmlformats-officedocument.spreadsheetml.sheet. Schema migrations run on startup via Liquibase.

Key features shipped

  • Commissions rule configuration UI (with conditional validation via Zod)
  • Excel export of commission reports (POI-backed, streaming)
  • IBM MQ integrations for the bank's downstream systems
  • OAuth2-secured REST API generated from a shared OpenAPI spec
  • Operator workflows on @fcc/ui, consistent with the rest of the bank's tooling

Outcome

The commissions product runs in production as part of Raiffeisenbank Russia's retail saving-products tooling. The fullstack-by-design engagement has held — the OpenAPI contract has eliminated the usual "frontend says X, backend says Y" friction that single-engineer projects normally hit when they touch both sides.

  • Live in production for the saving-products operators
  • Single OpenAPI spec, two codebases regenerating against it
  • One embedded topsweteam engineer carrying both halves of the workstream

Tech stack

Frontend: React 18, TypeScript, Vite, @fcc/ui v12.x, @fcc/icons, Redux Toolkit, react-hook-form, Zod, react-router-dom, styled-components, axios Frontend auth: oidc-client-ts, react-oidc-context Backend: Kotlin (JVM 21), Spring Boot, Spring Data JPA, Spring Security OAuth2 Resource Server, Spring JMS Backend data: PostgreSQL, Liquibase, preliquibase Backend integrations: IBM MQ (Jakarta JMS client), Apache POI (xlsx export) Backend ops: Springdoc OpenAPI, Micrometer + Prometheus Contract: OpenAPI 3, codegen on both sides (openapi-generator-cli typescript-fetch and Gradle openapi.generator) Build: Gradle (Kotlin DSL), npm, Vite Deploy: Helm (application + proxy charts), Kubernetes, Raiffeisen Artifactory

What we learned

  • A shared OpenAPI spec replaces the frontend/backend negotiation. When one engineer owns both halves, drift only happens when the contract isn't enforced. Generating both sides from the same spec is the cheapest way to enforce it.
  • Use the bank's plumbing, don't try to modernize it from inside the product. IBM MQ and Apache POI are not glamorous, but every other system in the bank already speaks them. Picking Kafka or CSV here would have been someone else's integration problem to solve.
  • Redux Toolkit is the right call when the operator workflow has cross-screen state. Form-local state (react-hook-form) and global state (RTK) compose cleanly; reaching for Redux from day one is the wrong default, but here the workflows justified it.

tech

build
Vite, Module Federation, Gradle (Kotlin DSL)
frontend
React 18, TypeScript, Redux Toolkit, Redux Saga, jotai, react-hook-form + Zod, formik + yup, styled-components, @fcc/ui (Raiffeisen design system)
backend
Kotlin, Java 21, Spring Boot 3.x, Spring Data JPA, Spring Cloud OpenFeign, Spring Web Services, Spring Boot Artemis (JMS), IBM MQ (Jakarta), Resilience4j
data
PostgreSQL, DB2, Liquibase, Flyway, Redis, Apache POI, PDFBox, ICU4J
auth
OIDC via react-oidc-context, OAuth2 Resource Server, OTP signing
microfrontend
single-spa-react, single-spa-css
contract
OpenAPI 3 codegen on both sides, wsdl2java for SOAP integrations, Springwolf for JMS
ops
Springdoc, Micrometer + Prometheus, OTel tracing, Sonar, Allure, JaCoCo, ShedLock
deployment
Helm, Kubernetes, Raiffeisen Artifactory, GitLab CI
type any key to open chatopen chat