Hello,
In the documentation for session: <a class="postlink" href="http://www.aspnetboilerplate.com/Pages/Documents/Abp-Session">http://www.aspnetboilerplate.com/Pages/ ... bp-Session</a>
You wrote that: "it's adviced to use IAbpSession in application layer and upper layers (we don't use it in domain layer normally). " advised *
However, to do multi-tenancy, aren't you taking the session and applying the TenantId somewhere in your Repository<Entity> ?
Is this a necessary evil? Please clarify if I missed something, thanks.
4 Answer(s)
-
0
Hi,
I'll explain it in detail since this is a very good question and would be very important to understand domain and app layers.
Think that you have a DiscountCalculationService (domain service) class which is used to calculate discount for a Product for a User (I assume the you can make different discounts based on user). It's Calculate method's signature should be like that:
double Calculate(Product p, User u);
It works (gets as parameter) with domain objects.
If you want to calculate it for CURRENT user, you should have such an APPLICATION service method.
double GetDiscountForCurrentUser(int productId) { var user = _userRepository.Get(AbpSession.GetUserId()); var product = _productRepository.Get(productId); return _discountCalculationService.Calculate(product, user); }
We would encapsulate productId into a DTO, doesn't matter in that case. As you see, it gets user from session and uses the domain service.
We would use the same DiscountCalculationService in another use case. Say that we want to calculate and compare discounts for 5 different users. In that case, we could get these users from repository, use _discountCalculationService.Calculate method and compare results.
In another case, we may need to same calculation in a WINDOWS Service for a particual user, where there is no Session at all.
As you see, making domain services session independent, they will be more reusable and will not assume there is a 'current user' in current context. Thus, we can share same domain logic between different kind of applications.
If we return to TenantId in multi-tenancy... ABP is a framework, an infrastructure code. So, a TenantId and current session is it's domain. So, we can use session in infrastructure code, which is already used to simplify our code by eleminating repeating code. But notice that ABP's infrastructure is carefully designed and it also works if there is no current tenant and current user.
In brief, Session is a presentation/application layer concept and our domain should be isolated from presentation layer as much as possible.
-
0
Sorry, my long reply was deleted (dam network) so I'll make this one short.
I see, so session wouldn't be used in domain, but the user in question may be passed up directly. Doesn't this make Application part of the presentation(Web layer?)
Disregarding that, my question is closer to "How did you implement multi-tenant filtering?".
I don't see where you do it inside your AbpRepositoryBase....
Thanks
-
0
Hi,
Application layer can get current user. Yes, the current user is actually known at UI layer and it's implemented in the UI, but can be used by application layer via IAbpSession abstraction. Different User Interfaces may implement it in different way, but it should be implemented in a way since application layer is intented to be used by UI and UI is for Users (USER interface :)).
Multitenancy is not implemented in repositories. It's implemented with EntityFramework.DynamicFilters library in DbContext.
-
0
<a class="postlink" href="https://github.com/jcachat/EntityFramework.DynamicFilters">https://github.com/jcachat/EntityFramew ... micFilters</a> ?
This is the final puzzle piece I was looking for to be able to fully embrace ABP. I have a complex system with 2 tenancy sides. (Our company, and other suppliers).
I'm sure I'm probably supposed to be creating a whole separate project for each side of the system, but for now I don't have time to make such huge changes.