multi-tenancy problem

Hi,
I need to have a different database for each tenant in a web application.
A tenant is an organization.
A logged user (member of an organization) can send commands to interact with application.
Both CommandBus and EventBus should be asynchronous.

During the web request (therefore request scope), a command dispatch interceptor inject the tenant (gathered from the logged user) in the command message.
Then the commandBus dispatch the command to the correct handler.

At this point the request scope is destroyed and the execution goes on in a different thread.
Then the command handler handles the command and the aggregate publishes events.
Those events are handled each one in its own thread.

I’m using CDI and everything works pretty well in the case of a single eventstore.

But what about selecting eventstore at runtime taking into account the tenant information stored in the command message?

Is it better to store tenant information in the metadata or in the command payload?

Whereas in the command part I have a command interceptor for injecting the tenant information in the command, in event part I have found nothing similar.
Is there a way to automatically provide that information to the event message?
Maybe should the tenant information be stored in the UnitOfWork?

I also thinked about using a bound session scope (or a similar custom scope), but I can realize neither a correct lifespan nor a correct way to manage it.

any ideas?

Alternatively, is there an automatic way to propagate some command metadata to event metadata?

Hi,

you can use the AuditingInterceptor on the Command Bus (as a Command Handler Interceptor) to automatically attach certain meta data to events.
To ensure required meta data is attached to the command, when dispatched, you can use a custom CommandGateway, where you annotate parameters with @MetaData. The values of those parameters will automatically be attached as meta data to the command that is dispatched. The AuditingInterceptor will then copy (some of) that meta data to all events produces as a result of that command.

Regarding your question about putting it in payload or meta data, if the tenant is a functional part of the command, then it should be put in the payload. If it just describes the context in which a command is being executed, then it should be in the meta data. For your use case, it sounds like the tenant should be part of the meta data . However, it is possible that from some commands, the tenant is both in the payload and meta data.

Hope this helps.

Cheers,

Allard

Thank you for your quick reply.
Now I can provide context to the events side.
But, playing around with this, I noticed that all events published from a single aggregate are handled in the same thread (i configured both asynchronous command bus and eventbus)
I was expected that every event was handled in its own thread.

To be more clear:

  • The command bus is AsynchronousCommandBus and the event bus is ClusteringEventBus with the DefaultClusterSelector configured with AsynchronousCluster.
  • CommandBus and EventBus use two different fixed thread pool executors with 10 threads each one
  • I have a command (CreateCommand) that produces an event (CreatedEvent)
  • There are 3 listeners for that event (ListenerA, ListenerB, ListenerC) where the thread name is logged after waited for 500ms

When two commands are dispatched, the console show something like this

pool-2-thread-1 - CreateCommand(context=123)
pool-2-thread-2 - CreateCommand(context=456)
pool-3-thread-1 - ListenerA(context=123)
pool-3-thread-1 - ListenerB(context=123)
pool-3-thread-1 - ListenerC(context=123)
pool-3-thread-2 - ListenerA(context=456)
pool-3-thread-2 - ListenerB(context=456)
pool-3-thread-2 - ListenerC(context=456)

As you can see commands are dispatched in their thread pool in different threads, events are handled in their thread pool, but NOT in defferent threads.

Instead I was expected somthing like this:

pool-2-thread-1 - CreateCommand(context=123)
pool-2-thread-2 - CreateCommand(context=456)
pool-3-thread-1 - ListenerA(context=123)
pool-3-thread-2 - ListenerB(context=123)
pool-3-thread-3 - ListenerC(context=123)
pool-3-thread-4 - ListenerA(context=456)
pool-3-thread-5 - ListenerB(context=456)
pool-3-thread-6 - ListenerC(context=456)

Where is my fault?

JDK - Innovation for Results
Alessio D’Innocenti Java analyst programmer
Phone: +39 06 8820 3706
Mobile: +39 393 044 8118
Email: alessio.dinnocenti@gmail.com
  • | - |

Get a signature like this: Click here!

Hi Alessio,

the asynchronous cluster can be configured with a ConcurrencyPolicy. It defines whiich events can be executed concurrently, and which must be handled sequentially. By default, Events from the same Aggregate are handled sequentially. That’s why you see those events being handled by the same thread.

You can use one of the pre-defined Concurrency Policies (full-sequential, fully concurrent of sequential-per-aggregate) or provide your own implementation.

Cheers,

Allard

Ok, thank you very much for your quick replys

JDK - Innovation for Results
Alessio D’Innocenti Java analyst programmer
Phone: +39 06 8820 3706
Mobile: +39 393 044 8118
Email: alessio.dinnocenti@gmail.com
  • |

Get a signature like this: Click here!

Hi Allard, one more question, please.

I made everything work in the CDI container.

I need that every command and event hadler, having context key specified in the metadata, runs in a scope identified by the context key.

I done the job and now it works fine, but I’m trying to optimize the code.

Now the integration is done by 3 parts:

  1. A command handler interceptor decorator that activate and destroy the scope
  2. A event handler decorator that activate and destroy the scope
  3. A work of unit listener that activate the context in the onPrepareCommit and destroy it in onCleanup

I’m thinking that maybe there is a better way to mark bounds of the context scope.

Is there something that I can monitor to figure out when scope starts (command handling) and stops (all events handled)?

Have you any idea?

I’m afraid that currently, this is the best way to go. This is one of the things I want to improve in Axon 3. There, each message handling task will be done in a separate Unit of Work. That Unit of Work will contain some correlation information which will be attached to all message created in the scope of that Unit of Work. Finally, you will be able to configure a listener to be attached to each Unit of Work, so that you can set all sorts of contextual information in the thread that will do the actual processing.

Cheers,

Allard