Real-World CMDx: Multi-Tenant SaaS Patterns
Part 4 of the Real-World CMDx series
Built on CMDx 2.0 — see the v2 release post. v2's frozen Result makes pattern matching on tenant metadata safe across threads, and the per-fiber chain isolates tenant scopes naturally.
Multi-tenancy changes everything. That simple Users::Register task you wrote? Now it needs to know which tenant it's operating on. Your database queries need scoping. Your logging needs tenant context. Your middleware stack needs to enforce tenant isolation. And if you get any of it wrong, Customer A sees Customer B's data, and you're writing an incident report instead of features.
I've built three multi-tenant SaaS products with Ruby and CMDx. Each one taught me something about where tenant boundaries belong and, more importantly, where they don't. The pattern I've settled on keeps tenant concerns out of business logic entirely — tasks don't know they're multi-tenant. The middleware and base classes handle it.