Eduin Garcia
Qollabi · Senior Software Developer & DevOps Engineer · Jan 2024 – Apr 2026

Strangler-fig migrationof a 268k-line platform

Qollabi is a multi-tenant B2B SaaS platform for partner and channel sales teams — account planning, objectives and key results (OKRs), joint business plans shared with partners, activities and campaigns, reporting periods, benchmarks, smart updates, bulk import and export, and CRM integrations.

Qollabi platform
Qollabi — platform scale3 repositoriesSheet 01
230
GraphQL queries
331
GraphQL mutations
273
GraphQL types
47
Event-sourced aggregates
65
MongoDB documents
163
Database migrations
60+
Console commands
170+
Test files

~268k lines of PHP across 3,700+ files, ~49k lines of TypeScript in the federated frontend, ~142k lines of JavaScript in the legacy frontend. This is the scale of the platform as I inherited it; my own work is in section 01.

01 · My role

Frontend · backend · platform

02 · Architecture

Strangler fig, enforced in CI

03 · Repositories

Backend · remote · host
qollabi-sfa-core

Symfony/GraphQL backend, event store, projections, workers, CLI, cron.

qollabi-rja-federation

Module Federation remote: app shell, feature modules (integration-layer, smart-updates, tableau-demo, common), shared packages and Storybook.

qollabi-rja-brm

Mounted as legacy/ — the original React 16 SPA, now the federation host.

04 · Backend

PHP 8.2 · Symfony 5.4
Runtime
PHP 8.2, PHP-FPM
Framework
Symfony 5.4 (Flex, Framework/Security/Twig bundles)
Database
MongoDB 7 (replica set), Doctrine MongoDB ODM 2, devture/mongodb-migrations
API
GraphQL — custom qollabi/sfx-graphql-bundle, field-level authorization voters
CQRS
League Tactician (command/query/event buses) + Symfony Messenger with per-bus middleware
Event sourcing
EventSauce 0.8 + Doctrine message repository, projector layer for read models
Queues
RabbitMQ 3 (ext-amqp, kcs/messenger-extra) — quorum queues, dead-letter exchanges, delivery limits, dedicated integration-layer transport
Cache / locks
Redis 5 (predis, snc/redis-bundle), Symfony Lock & Cache
Auth
Auth0 (auth0-php, Management API), firebase/php-jwt, S3-distributed JWT certs
Storage
AWS SDK for PHP + Flysystem (S3 adapter, oneup/flysystem-bundle)
Email
Symfony Mailer + SendGrid, Twig with Inky, CSS inliner and Markdown extras
Spreadsheets
PhpSpreadsheet — bulk account/objective import & export pipelines
i18n
php-translation/symfony-bundle + willdurand/js-translation-bundle (catalogs shared with the frontend)
Feature flags
Opensoft Rollout (qollabi/phx-rollout-bundle)
Observability
Rollbar, Monolog, Blackfire SDK, Symfony Stopwatch/Profiler
Quality
PHPSpec 7, PHPUnit, Symfony Panther, PHPStan level 6, phpat architecture rules, PHP-CS-Fixer, Rector

05 · Federated app

React 18 · TypeScript 5.3
Core
React 18, TypeScript 5.3, Webpack 5
Federation
@module-federation/enhanced — remote exposing modules & components to the legacy host
Monorepo
Yarn workspaces — 4 feature modules, 11 shared packages, 6 shared build configs, 1 Storybook workspace
Components
Mantine 7 (core, dates, hooks, notifications), mantine-datatable, PrimeReact 10 — wrapped in a shared @qollabi/ui design system
Styling
Tailwind CSS 3, PostCSS (postcss-preset-mantine), Sass, class-variance-authority, clsx, tailwind-merge
Server state
TanStack Query 5 (suspense queries) over graphql-request + TypedDocumentNode generated documents
Client state
Zustand 5
Forms
React Hook Form 7 + Yup resolvers
i18n
i18next + react-i18next (shared @qollabi/i18n)
Dates
Day.js and Luxon
Icons
FontAwesome Pro (light/regular/solid), Tabler Icons
Misc
react-error-boundary, notistack, SortableJS, Zod (build-time env-var validation)
Tooling
Storybook 7, Jest + ts-jest, ESLint + Prettier shared configs, Husky

06 · Legacy app

React 16 · the federation host
Core
React 16, Webpack 5 (migrated off CRA), Babel, partial Flow typing
Data
Apollo Client 2 / react-apollo 3, GraphQL, 70 .graphql documents, batched HTTP link
Components
Ant Design 3 (Less theming), PrimeReact 5
Routing
React Router 5
Forms
Final Form + react-final-form (arrays, focus, field-data), React Hook Form 5, Yup, validator
Rich text
TipTap 2 (mentions, links, lists) and Slate 0.82
Interaction
react-beautiful-dnd, react-dnd, react-sortable-hoc, react-virtualized, react-dates
Charts
Recharts 1
Dates
Moment + moment-timezone + moment-range, @vvo/tzdb
Auth
Auth0 React SDK, jsonwebtoken
Analytics
Rollbar, FullStory, Smartlook, Intercom
Testing
Cypress 4 + Mocha JUnit reporters, Storybook 7

07 · Infrastructure & DevOps

Swarm · GitLab CI · Ansible

08 · Engineering highlights

Six decisions
Runtime micro-frontend bridge

A remoteLazyComponent / remoteLazyModule HOC pair on the legacy side lazily pulls federated exports and injects host context (language, router history, tenant, translation mode), so new React 18 modules render as first-class citizens inside a React 16 app.

Architecture tests as migration policy

The strangler-fig boundary is a build-breaking rule, not a convention, which keeps a 268k-line legacy codebase from re-entangling itself.

Outbound integration layer

18 typed routing keys over a direct RabbitMQ exchange, each with quorum queues, dead-letter routing and a custom message serializer, pushing plans, objectives, activities and access control to external CRMs.

Salesforce OAuth in the browser

The integration is configured per tenant with no server-side credential store, so the flow runs client-side — credentials are encrypted before the token ever reaches browser storage.

Projection replay tooling

CLI commands to rebuild read models, replay benchmark history and repair event versions against a live event store.

Per-customer batch orchestration

A single cron entry fans a console command across every tenant database, so scheduled work scales with customer count without extra infrastructure.

Qollabi integrations
Qollabi product overview
Previous: Qollabi 2.0Next: HolaPlace