Software Architect — cloud-native distributed systems.
Ten years of designing and building platforms across banking, healthcare, retail and B2B SaaS. Currently Software Architect at TeamViewer, working on platform and infrastructure foundations for the DEX platform. Before that, solution architecture for digital banking and payments, and a technical lead role on a healthcare claims adjudication platform.
Most of what I do sits in four places: multi-tenant SaaS and the isolation model underneath it, identity and access (Enterprise SSO, OIDC, SCIM provisioning, scoped-token authorization), event-driven architecture on Kafka, and workflow orchestration with Camunda BPMN and the Saga pattern. Plus the unglamorous half — cloud migrations, multi-region disaster recovery, and taking the cost out of a cluster that grew without anyone watching.
TenantLayer — the tenant isolation layer
for Spring Boot and Postgres. Your query has no WHERE tenant_id. It returns only your
tenant's rows anyway.
@WithTenant("acme")
@Test
void oneTenantCannotSeeAnother() {
assertTenantCannotSee("globex");
}Postgres already has row-level security, and the database doing the enforcing is exactly why
it is trustworthy. TenantLayer is everything around it: resolving the tenant from a header,
subdomain, path or JWT claim; keeping it alive across @Async, virtual threads, scheduled
jobs, outbound HTTP and Kafka; three isolation strategies behind one configuration switch;
and a test kit that refuses to pass for the wrong reason.
It came out of writing the same tenancy code by hand on more than one platform, and getting it subtly wrong in a different way each time. Apache 2.0, on Maven Central, documented at tenantlayer.io.
A bug worth knowing about. Setting spring.threads.virtual.enabled=true makes Spring Boot
build a SimpleAsyncTaskExecutor instead of a ThreadPoolTaskExecutor — and a task decorator
registered only for the pooled one disappears with it. One property, widely recommended, with
no mention of tenancy anywhere near it, and every @Async method silently loses its tenant.
Nothing throws. The query just returns no rows, and it surfaces days later as missing data.
Most multi-tenancy bugs are like that: they do not crash, they return the wrong rows, on the
paths nobody tests.
- Multi-tenant MSP platform — one provider managing many customer organisations from a single pane, with strict per-customer data isolation, shared and isolated operating modes, and first-class partial-failure handling across regions.
- Cloud-native migration of a credit assessment platform — monolith to 20+ microservices on Azure with DDD boundaries, API gateway routing, Kubernetes autoscaling, Kafka messaging and an observability stack.
- Card dispute resolution — BPMN orchestration of chargeback lifecycles across customer, merchant and bank, with Saga-based payment reversals and event sourcing for compliance.
- Multi-region active-passive DR — warm standby with health-check failover, cross-region replication and pre-provisioned capacity. RTO under 5 minutes, RPO 30.
- AI document verification for cross-border payments — OCR ingestion, a classification and validation engine, LLM-backed regulatory checks and model-scored risk.
Java and Spring Boot mainly, plus Python and some C++ when the JVM is the wrong tool. PostgreSQL, Kafka, Redis. Azure and AWS, Kubernetes, Terraform. Microsoft Certified: Azure Solutions Architect Expert (AZ-305).
Also here: SAGA over Kafka, Camunda workflows, order processing, low-latency C++.
TenantLayer takes pull requests, and there are good first issues. One rule matters more than the rest: every isolation claim is mutation-tested. Break it, watch the test fail, then fix it. A test that cannot fail is not evidence.


