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:
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
// links
