Multi-Tenancy with Data Protection
Axoniq Data Protection encrypts selected fields while a message is converted to or from bytes. In a multi-tenant application, provide a separate converter for every tenant so the converter uses that tenant’s crypto engine and key store. This keeps key management separate even when two tenants use the same data-subject identifier.
The converter is selected when Axon Framework creates infrastructure for a known tenant. It is then used for command and query payloads, event storage, snapshots, and persistent-stream events. The tenant is not added to event metadata and does not need to be held in application state.
Marking protected fields
Mark the data-subject identifier and the fields to protect in the event payload.
The data-subject identifier selects the encryption key; @PersonalData marks a field for encryption.
/*
* Copyright (c) 2010-2026. AxonIQ B.V.
*
* Licensed under the AXONIQ TERMS OF SERVICE,
* Version 29 April 2026 (the "License");
*
* The software is available for evaluation use without registration.
* Continued use beyond the evaluation period requires registration
* and a commercial license. See the License for the specific language
* governing permissions and limitations under the License.
* You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at:
* https://www.axoniq.io/legal/terms-of-service
*
* For licensing information and to register, visit:
* https://www.axoniq.io/pricing
*/
package multitenancy.dataprotection;
import io.axoniq.framework.dataprotection.api.DataSubjectId;
import io.axoniq.framework.dataprotection.api.PersonalData;
import org.axonframework.messaging.eventhandling.annotation.Event;
@Event(namespace = "example", name = "CustomerDataRecorded", version = "1.0.0")
public record CustomerDataRecorded(
@DataSubjectId String customerId,
@PersonalData String email
) {
}
Providing a converter per tenant
Register one tenant-scoped component provider for Converter.
Its factory is called for a tenant when that tenant first needs a converter.
Create a FieldEncryptingConverter with the crypto engine for that tenant and a normal delegate converter.
The crypto-engine factory is application-specific.
It must select a key store or key namespace that is isolated for the supplied TenantDescriptor; a shared engine that maps equal data-subject identifiers to the same key does not provide tenant isolation.
-
Declarative - Configuration API
-
Spring Boot
Register the provider on the application’s MessagingConfigurer:
public void registerTenantConverters(MessagingConfigurer configurer) {
configurer.componentRegistry(registry -> registry.registerComponent(
TenantComponentProvider.class, (1)
configuration -> TenantComponentProvider.withFactory(
Converter.class, (2)
tenant -> new FieldEncryptingConverter(
cryptoEngines.createFor(tenant), (3)
new JacksonConverter()
)
)
));
}
| 1 | Register the TenantComponentProvider so multi-tenancy can discover it. |
| 2 | The provider supplies Converter instances. |
| 3 | Build the crypto engine for the tenant that the provider is creating a converter for. |
Expose the provider as a Spring bean:
@Bean
public TenantComponentProvider<Converter> tenantDataProtectionConverters(
DeclarativeDataProtectionConfiguration.TenantCryptoEngineFactory cryptoEngines
) {
return TenantComponentProvider.withFactory(
Converter.class, (1)
tenant -> new FieldEncryptingConverter(
cryptoEngines.createFor(tenant), (2)
new JacksonConverter()
)
);
}
| 1 | The provider supplies one converter per tenant. |
| 2 | Build the crypto engine for that tenant before constructing its encrypting converter. |
The provider creates and caches each converter lazily, and releases it when its tenant is removed. See tenant-scoped component lifecycle for the lifecycle details.
What remains global
Registering a tenant-scoped Converter does not replace the application’s normal global converter.
The global converter remains in use for work that has no tenant, while the tenant-scoped converter is used wherever multi-tenancy has already resolved the tenant.
If no tenant-scoped converter is registered, multi-tenancy uses the normal global converter for every tenant.
For data-protection configuration beyond tenant scoping, including crypto-engine selection and key deletion, see the Axoniq Data Protection reference documentation.