Skip to main content

// experience

Professional Experience

2023 · Present

Kuubix BV

Full-Stack Developer & Co-owner

01. The Company

Kuubix is a software company founded by my brother focused primarily on tooling for the furniture and interior industry. As co-owner I wear all the hats: architecture decisions, backend and frontend development, infrastructure, and customer relations (B2B customer meetings), and stay in direct contact with clients throughout a project. Meetings have been in Dutch, English, and occasionally with German-speaking contacts where I follow along and adapt accordingly. The main product is Configbox: a multi-tenant SaaS platform that handles product configuration and quotation for furniture retailers.

Running a small software company means the architectural decisions you make are vital. A good decision on one day could turn another sour. That pressure has been good for me. It forces you to think seriously about trade-offs between flexibility and complexity rather than just picking whatever seems cleanest.

02. Configbox

Configbox lets furniture retailers manage their product catalogue, configure custom items with customer-specific pricing rules, and generate quotes, all from one platform. The multi-tenant model means each customer gets their own isolated environment without running separate deployments.

The frontend is built in Vue 3 + Tailwind CSS, the backend in Laravel, with PostgreSQL as the primary database, Redis for caching and job queues, and the whole stack containerised with Docker behind nginx, with CI/CD through Jenkins.

03. Multi-Tenant Database Architecture

The trickiest part was the multi-tenant database setup. Each customer needs proper data isolation, but we didn't want to run a separate deployment per client. The solution uses three logical databases:

  • central

    Stores the registry of customer database connections. Credentials (host, database name, username, password, schema) are AES-encrypted at rest; the encryption key lives only in the application's .env.

  • cas

    Central Authentication System: a shared database for user accounts and session management, decoupled from any single tenant's data.

  • app

    Per-customer application database (isolated by PostgreSQL schema), containing all business data: catalogue, pricing, quotes. Unreachable without going through the central credential lookup.

At runtime, a DynamicDatabase trait handles switching the active connection. After authenticating through CAS, the system looks up the user's assigned database name, decrypts the credentials, and rewires Laravel's database layer on the fly:

DynamicDatabase.php: connection switching
public function setDynamicDatabaseConnection(DatabaseConnection $connection): void
{
    $key      = config('database.encryption_key');
    $host     = decryptWithKey($connection->host, $key);
    $database = decryptWithKey($connection->database_name, $key);
    $username = decryptWithKey($connection->username, $key);
    $password = decryptWithKey($connection->password, $key);
    $schema   = $connection->schema_name
                  ? decryptWithKey($connection->schema_name, $key)
                  : null;

    $connectionString = ['driver' => $connection->driver, ...];

    // Optimisation: avoid a full reconnect when staying on the same server
    if ($currentConnection['host'] == $host && $currentConnection['port'] == $port) {
        Config::set('database.connections.dynamic', $connectionString);
        Config::set('database.default', 'dynamic');
        // Only swap the schema if it changed
        if (isset($schema) && $currentConnection['schema'] !== $schema) {
            Config::set('database.connections.dynamic.schema', $schema);
        }
    } else {
        Config::set('database.connections.dynamic', $connectionString);
        Config::set('database.default', 'dynamic');
        DB::purge('dynamic');
        DB::reconnect('dynamic');
    }
}

Connection lookups are cached in Redis with a 24-hour TTL, so the central database isn't hit on every request. The same trait has a loopOverAllCustomerDatabases method for background jobs: scheduled exports, platform-wide migrations, anything that needs to touch every tenant in order.

04. FileMaker Integration via JDBC

Some client work involved existing data living in FileMaker, a legacy database platform with limited SQL support and no real way to do joins. The goal was to get that data into a modern PHP app without migrating it.

The solution was a PHP service running in Docker that connects to FileMaker over JDBC (FileMaker exposes JDBC and ODBC drivers). On top of that I built a query layer with a Laravel Eloquent-style interface, so calling code could do chainable queries and get proper result sets back. Joins that FileMaker couldn't handle natively were done in PHP.

It ended up as a clean data layer that let us query FileMaker like a normal database, without changing anything the client already had set up. And if they ever move off FileMaker, only the adapter needs to change.

05. Mentoring Interns

I've guided a few different interns across different projects and stacks. The approach was always the same: get them writing real code as quickly as possible, and actually explain why things are done a certain way rather than just telling them what to do.

Unity VR app + .NET backend

Guided students through building a VR application demo for a client in Unity alongside a .NET backend. Covered database design basics, project structure, and got linting and formatting standards in place early so the codebase stayed consistent as the team grew.

Vue 3 frontend

Worked closely with an intern on a Vue project, focusing on resilience and discipline: client-side caching strategies, graceful handling of database failures, the habit of wrapping anything that can realistically fail in try/catch rather than only happy-path code, being careful about commenting out code instead of deleting it, and proper Git branch and commit hygiene.

Vue 3 + Laravel

Mentored an intern through a full-stack project on the same Vue 3 and Laravel stack used in Configbox, helping them navigate the full request lifecycle from frontend state to API design to database layer.

Dolibarr extensions

An intern arrived with a single large PHP file as their first attempt at a Dolibarr extension. Over five months I walked them through the full shift: splitting logic into directories, writing OOP PHP properly, understanding separation of concerns, and thinking about code as something a future reader has to maintain. By the end they said they'd learned more in those five months than in two years of school, which is the best outcome this kind of mentoring can have.

// technologies

LaravelVue 3Tailwind CSSPostgreSQLRedisDockernginxJenkinsFileMakerUnityC#WordPressDolibarr

Feb 2023 · Jun 2023

Vitra Partner Store Antwerp

Internship · Web Developer

01. The Company

At the end of February 2023 I started my internship at Vitra Partner Store Antwerp (VPSA). VPSA is a partner store of the Vitra brand, located in Antwerp, and operated by Master Meubel Turnhout, another furniture store in Turnhout where I spent most of my development time.

I worked on the project almost entirely solo, with technical support from external German partner Eastern Graphics, who built the configurator system I was integrating. Coordination with their team meant following technical discussions partly in German and communicating back in English.

02. The Project

My task was to integrate pCon by Eastern Graphics (a 3D product configuration tool) into the VPSA WordPress webshop, letting customers fully configure materials, colors, and dimensions before checkout. Built in PHP and JavaScript, hooking into both WordPress and Eastern Graphics' pCon API.

VPSA logo

// technologies

PHPJavaScriptWordPresspConAWS