Multi tenancy support in Java EE 6

2020-06-08 13:25发布

I have an existing Java EE 6 application (deployed in Glassfish v 3.1) and want to support multiple tenants. Technologies/APIs I'm currently using in my app are

  • EJB (including the EJB timer service)
  • JPA 2.0 (EclipseLink)
  • JSF 2.0
  • JMS
  • JAX-RS
  • I plan to use CDI as well

As far as I know, adding multi-tenancy support affects only the persistence layer. My question: Has anybody done this before? What are the steps to convert the application? Will this affect other layers other than persistence?

There will be a high number of tenants, therefore, all data will reside in the same DB schema.

4条回答
一纸荒年 Trace。
2楼-- · 2020-06-08 13:35

Tell us about the number and the degree of separation and customization necessary for different tenants.

If you have a small number of tenants, I would propose to create a customizable "white-label" product. This gives you the opportunity to create some specific things for one tenant without overcomplexing matters. Plus, separating the applications per tenant helps you in maintenance. We did this for a product with a handful of different tenants.

If you have many tenants, this is of course no longer practical. We did a generic version of the same product. All we did then was distinguish tenants by id after login, thus separating the data from others. But still, there was nothing to do in terms of changing the application or a layer within, the id was all what was needed to separate the data and the workflow is automatically separated by having different instances of beans or other managed objects.

查看更多
老娘就宠你
3楼-- · 2020-06-08 13:38

Persistence Layer

Start with the persistence layer. Roll upwards through your architecture once you have that done.

The Schema that you are proposing would have an ID that identifies the tenant (eg. TenantId). Each table would have this ID. In all of your queries you would have to ensure that the TenantId matches the logged in User's TenantId.

The difficulty with this is that it is a very manual process.

If you go with Hibernate as your JPA provider then there are some tools that will help with this; namely Hibernate Filters.

These are commonly used to restrict access on multi-tenant Schemas (see here and here for some more)

I haven't used EclipseLink but it does look like it has good support for Multi-Tenancy as well. The DiscriminatorColumn looks like a very similar concept to Hibernate Filters.

Service Layer

I assume that you're using JAX-RS and JMS for a Service Layer. If so then you will also need to think about how you are going to pass the tenantId around and authenticate your Tenants. How are you going to prevent one tenant from accessing the REST service of another? Same thing for JMS.

UI Layer

You are going to have to hook up your login in your UI to a Bean (Hibernate or Eclipselink) that sets the TenantId for the Filter/Discriminator.

查看更多
唯我独甜
4楼-- · 2020-06-08 13:44

Go message oriented.

If you choose messaging as the strategic approach and refactor (if necessary) business logic around JMS, then other options remain viable and locally applicable.

With this approach, you pay a specific fixed cost (refactor) in your existing (single tenant) system. You then can apply approaches of various degrees of complexity, ranging from simple sharding (@Geziefer's id based association) to a full blown shared-core-schema + extended-tenant-specific-schemas approach, without impacting system architecture and additional refactoring.

You will further have orthogonal control over your system data flows via the messaging layer (applying routers, filters, special processing paths, etc.)

[edit per request]

There is nothing per se in M.T. that explicitly suggests message orientation. But as a general problem, we are looking at widening interfaces, and enriched data flows. Per an API based approach, you would need to carefully inject the appropriate the tenant discriminant in all required interfaces (e.g. methods). A message based (or alternatively a context based API approach) allows for a normative (stable) interface (e.g. message.send()) and at the same allows for explicit specialized data flows. If switching to a message based backbone is not on the table, you are strongly suggested to consider injecting a uniform context (e.g. "RequestContext") param in your APIs. This single extension should cover all your future specialization needs.

查看更多
相关推荐>>
5楼-- · 2020-06-08 13:55

There's several ways you can go with this, depending on the level of separation you want to achieve and how many concurrent tenants you want to support. At one extreme, you can create a new schema for each tenant and therefore ensure database-level isolation of data. For most practical purposes it's usually sufficient to have a logical partitioning of your data by assigning a tenant_id to every entity in your domain model and maintaining foreign-key constraints. Of course this means you'll probably want to always pass in your current session's tenant_id to every query / finder method so that it can restrict the data set based on that. You'll want to make sure that users cannot access another tenant's data by entering a tenant id (or a entity id) that does not belong to them in url.

查看更多
登录 后发表回答