Inspiration

Every mid-size company eventually hits the same wall: leave requests live in one spreadsheet, purchase approvals live in email threads, and IT/HR/finance workflows are duct-taped together with forms nobody can audit. Off-the-shelf SaaS tools solve one of these problems, but not all of them, and almost none of them let a single vendor serve multiple client organizations securely from one codebase. I wanted to build the platform I kept wishing existed: a multi-tenant "operating desk" where any organization can spin up its own branded workflow environment — requests, approvals, leave, documents, and reporting — without touching code.

What it does

FlowDesk is a multi-tenant enterprise workflow automation platform with four cooperating layers:

  • End-User Interface — staff submit requests, track status, attach files, comment, and receive notifications.
  • Tenant Admin Interface — organization admins configure branding, departments, managers, users, roles, approval chains, and leave rules for their own tenant, fully isolated from every other tenant.
  • Super Admin Interface — platform owners onboard new tenant organizations, approve tenant admins, monitor activity, and manage global settings.
  • Core Workflow Engine — a shared backend engine that drives request lifecycles (submitted → pending approval → approved/rejected/escalated → implemented → completed), enforces approval modes (any-one, all-required, majority, sequential, parallel), and writes an immutable audit trail for every action.

On top of that sits leave management (entitlements, balances, conflict detection, manager/HR approval), configurable request types, file attachments via object storage, real-time dashboards, and full observability so operators can see the health of the platform itself.

How I built it

  • Frontend + BFF: Next.js (App Router) with React, TypeScript, Tailwind CSS, and a glassmorphic component system. The browser never talks to the backend directly — every request goes through a Backend-for-Frontend layer built with Next.js Route Handlers, which owns HTTP-only cookie sessions, CSRF protection, and token refresh, then proxies to the Java API as server-only calls.
  • Backend API: Java + Spring Boot (Spring Web, Spring Security with OAuth2 Resource Server, Spring Data MongoDB, Spring Validation, Spring Actuator) split into bounded services — tenant/identity, workflow, and reporting — each independently runnable and independently secured.
  • Identity & tenancy: Keycloak issues JWTs per realm; every backend endpoint validates the token and enforces tenant isolation at the query level, so no tenant can ever see another tenant's data.
  • Data & messaging: MongoDB for domain documents (tenants, departments, users, requests, workflows, audit logs), Redis for caching/rate-limiting, MinIO for file storage, and Kafka wired in for asynchronous, decoupled event handling.
  • Observability: Prometheus scrapes metrics from all three Spring services, visualized through custom Grafana dashboards (API health, JVM, and a platform-wide overview) so I can watch the system I built, not just the product it powers.
  • Everything containerized: Docker Compose brings up Mongo, Redis, MinIO, Keycloak, Kafka, Prometheus, and Grafana with one command, so the whole stack runs identically on any machine.

Challenges I ran into

  • Strict tenant isolation without duplicating infrastructure — I had to guarantee that a bug in one query could never leak another tenant's departments, users, or requests, which meant building tenant-scoping into the data-access layer itself rather than trusting every controller to remember it.
  • Keeping the browser fully decoupled from the backend — routing 100% of traffic through the BFF meant re-implementing concerns like token refresh, CSRF, and error normalization that a direct client-to-API call would get "for free," in exchange for a much smaller attack surface.
  • Modeling a workflow engine flexible enough for approvals, leave, and generic requests — I needed one state machine (draft → submitted → approval → hold/escalate → implement → complete) that could express wildly different business processes without hardcoding any of them.
  • Making the platform observable from day one — wiring Prometheus/Grafana in early, rather than bolting it on later, forced us to think about metrics and audit logging as first-class features instead of an afterthought.

Built With

  • docker
  • docker-compose
  • framer-motion
  • grafana
  • java
  • kafka
  • keycloak
  • minio
  • mongodb
  • next.js
  • oauth2
  • prometheus
  • react
  • react-hook-form
  • recharts
  • redis
  • spring-boot
  • spring-data-mongodb
  • spring-security
  • tailwind-css
  • tanstack-query
  • typescript
  • zod
  • zustand
Share this project:

Updates