{ aobarrydev }
Back to projects// case study

Store — SaaS ERP

A multi-tenant ERP that lets retailers run sales, purchasing, stock and subscriptions end-to-end — without paperwork.

Store ERP — public landing page
17business modules
~215REST endpoints
49domain entities
741automated tests
85+RBAC permissions
49Flyway migrations

Context

Many retailers still run their business on notebooks or spreadsheets: rough stock counts, invisible margins, no traceability. Store set out to deliver an accessible, affordable web and mobile ERP covering the full retail cycle — from restocking to checkout — while staying easy to adopt.

It is a SaaS product: each company gets its own isolated space, can run several stores and employees with distinct roles, and subscribes to a plan. I designed and built the entire backend and the system architecture.

Architecture

The backend follows a Domain-Driven Design approach: each of the 17 business modules is a self-contained context, split into four strict layers. This separation keeps the domain core independent from the framework and infrastructure details, and makes every module testable and maintainable in isolation.

domainBusiness core: entities, rules, repository interfaces (ports).
applicationUse-case orchestration, DTOs and mappers.
infrastructureSpring Data adapters, JPA specifications, external integrations.
presentationREST controllers, input validation, OpenAPI documentation.

Multi-tenant isolation

Isolation relies on an application-level strategy rather than a schema-per-tenant setup. On login, a JWT carries the tenant identity (company and store), role and permissions. Every query is then systematically scoped to that perimeter, guaranteeing a company never reaches another's data.

The perimeter is hierarchical: the company is the root tenant, and each company can run several stores. A single PostgreSQL database holds all data — a deliberate choice that simplifies operations and keeps costs in check at the target scale, while leaving the door open to stronger isolation if needed.

JWT · entrepriseId · magasinId · permissions
entreprise (tenant)
magasin 1magasin 2magasin n

Frontend & full-stack consistency

The frontend (Next.js 16 / React 19 in TypeScript) follows the same discipline as the backend: a feature-based layout mirroring the business modules, and a shared layer split into domain / application / infrastructure / presentation. This front/back symmetry keeps the system predictable — the same boundaries appear across the whole stack.

Server data is handled with TanStack Query (cache, invalidation), client state with Zustand, and forms with React Hook Form validated by Zod. The UI is built with Tailwind CSS and shadcn/ui, is bilingual (next-intl) and installable as a PWA — delivering the web and mobile access promised to retailers.

Key modules

  • Sales & checkout

    Full order cycle, FIFO stock consumption, instalment payments, anonymous walk-in customers and till summaries.

  • Purchasing & suppliers

    Supplier orders from draft to receipt, multiple receptions, traceable batches and split payments.

  • Stock & inventory

    Batch tracking with expiry dates, average-cost valuation, movement journal and physical inventories with profit/loss reports.

  • SaaS subscriptions

    Plans (Trial, Standard, Pro) with limits, coupons and promotions, manual payment validated against proof (Wave, Orange Money).

  • Security & RBAC

    JWT authentication, refresh tokens, data-driven access control: 85+ permissions and roles customisable per company.

  • Reporting & audit

    Dashboards (top products, margins, stock valuation) and an audit log whose scope adapts to the user's role.

Challenges & solutions

  • Challenge

    Model very different users — an ADMIN running the platform, an owner running their company, employees tied to its stores — each linked to distinct entities, without multiplying authentication or authorization mechanisms.

    Solution

    Separate identity from access. A Person → Utilisateur hierarchy (JOINED inheritance) captures who the user is and what they belong to: the owner carries their company, the employee their store. Facing it, a single Account entity holds the credentials and points to a role bearing permissions. The result: admin, owner and employees all authenticate through the same entry point and are authorized by the same RBAC — every endpoint checks a permission, never the user's concrete type. System roles stay non-delegable, and the resolved scope (company, store) is projected into the JWT for uniform authorization.

  • Challenge

    Guarantee that no company can ever see another's data on a shared database.

    Solution

    The tenant (company + store) is carried in the JWT and enforced on every query through the security context. Perimeter scoping is centralised to prevent leaks by omission.

  • Challenge

    Know the stock value at any time and trace every unit, including perishable goods.

    Solution

    A FIFO batch stock model (lot number, expiry date, purchase price) with a timestamped movement journal and expiring-batch detection. Valuation uses the average purchase price.

  • Challenge

    Offer fine-grained, role-specific permissions without recompiling for every new need.

    Solution

    A data-driven RBAC: permissions and roles live in the database, roles are customisable per company, and each endpoint declares the permission it requires.

  • Challenge

    Collect subscriptions while local payments go through Wave, Orange Money or transfer, outside the app.

    Solution

    A manual-payment subscription flow: the customer uploads proof, an admin approves or rejects, and activation is computed (plan, duration, promotion, coupon). The model is ready for an automated gateway when the time comes.

  • Challenge

    Keep quality high across a broad scope and keep the database under control.

    Solution

    741 tests run against a real PostgreSQL database, a SonarQube quality gate (≥ 80% coverage on new code), a schema versioned by 49 Flyway migrations, all shipped through a GitLab CI pipeline and a multistage Docker image.

Tech stack

Backend
  • Java
  • Spring Boot
  • Spring Security
  • Spring Data JPA
  • JWT
Data
  • PostgreSQL
  • Flyway
Frontend
  • TypeScript
  • React
  • Next.js
  • Tailwind CSS
  • shadcn/ui
  • TanStack Query
  • Zustand
  • React Hook Form
  • Zod
  • PWA
Quality
  • JUnit 5
  • Mockito
  • JaCoCo
  • SonarQube
  • Vitest
Ops & delivery
  • Docker
  • GitLab
  • CI/CD
  • Railway
  • Vercel

Want to know more?

I can walk you through the architecture in detail and answer your technical questions.

Contact me