Hi
I've been developing a module (let's call this "FooModule") to integrate with my base solution (let's call this "BaseModule").
I am currently trying to figure out how to invoke ModuleFoo entity migrations from ModuleBase (my base solution) when a new Tenant is created.
I noticed that, in my generated base solution BaseModule, the generated TenantManager class invokes the DbMigrator class of the base solution to trigger database creation and migration for the tenant, whenever a new tenant is created. This functionality is located in BaseModule's core/domain layer project.
I also studied the example blog module solution to see how module migrations should be handled.
I noticed a TenantManager class has been created inside the module's core/domain layer project. The example application that consumes the module, though, is a simple console application without any entities of it's own. As such it does not properly demonstrate how a module with entity migrations would be properly integrated into a base solution, with DDD-type layering, that has migrations of its own.
I am thus tryng to figure out:
- Is there a convention or structure at play here where each module should have it's own tenantManager class?
- If not, this would mean that I would have to call code from my Module's domain layer from the domain layer of my base solution to perform this function? This would create a circular dependency, since my module relies on multitenancy and identity interfaces in my base solution.
10 Answer(s)
-
0
One possible approach I have thought of, is to do the following:
- Create an
IFooTenantManager: IDomainService, ITransientDependencyinterface in myFooModule.Core.Sharedsubmodule, and an implementationFooTenantManager: ITenantManagerinFooModule.Coresubmodule.
I can then use constructor injection in BaseModule to inject
IFooTenantManager, as it will resolve by convention to a concrete instance ofFooTenantManager.Would this be the best approach?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) - Create an
-
0
Hi, we don't usually use interface for manager in the project thus far. However, it doesn't mean it is wrong to use interface for manager.
are you trying to listen to tenant creation and perform migration i your FooModule? if so, have you considered triggering migration when tenant entity domain event occurs?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Thanks for the feedback, @ryancyq
I have considered domain events.
However, this will mean that each module migration will run asynchronously, which I think would create challenges in the following regards:
- Tenant database creation/migration should occur in a single transaction. I think, but am not sure, that should a portion of this happen in an event handler (asynchronously) it will not form part of the transaction / unit of work. In the event that this fails for one of the modules, it could create issues since a full rollback will not occur.
- User feedback - It will cause issues if the new tenant continues using the system if all module migrations has not yet completed successfully.
- Controlling whether all handlers and their contained migrations fired and executed successfully could become a complex affair.
But perhaps there is an easy way to address these issues that I am not seeing?
In the meantime, I have implemented it as I described above and it works for now (but suggestions for better / correct ways are always appreciated)
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
It might also be useful, in future, to have the ability to register a
MigrationProviderduring the pre-initialize method of your module, which will then be invoked whenever your aplication needs to perform migrations.For eaxample:
public class MyEfCoreModule : AbpModule { public override void PreInitialize() { Configuration.Migrations.Providers.Add<MyEfCoreModuleMigrationProvider>(); } public override void Initialize() { IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly()); } }This could work in a similar fashion than NavigationProviders currently does. This will remove the need for any directly-coupled code in dependent modules.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
From https://aspnetboilerplate.com/Pages/Documents/EventBus-Domain-Events#entity-changes:
'ing' events (e.g. EntityUpdating) are triggered before committing a transaction. This way, you can rollback the unit of work and prevent an operation by throwing an exception. 'ed' events (e.g. EntityUpdated) are triggered after committing a transaction, for which you cannot rollback the unit of work.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
@aaron As far as I can tell that convention is limited to entity changes (inserts, updates and deletes), not domain events.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
What do you think the difference is?
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Entity events are generic events that react to record create, update and delete events on entities. The scope of your event / UoW is thus limited.
The scope of our UoW is not limited to entity record create/update/delete for one entity at a time. The scope of our UoW is about performing schema updates and seeding across multiple entities, not a single record update.
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
The scope of our UoW
Do you mean the scope of your migration?
Your question title clearly mentions "on tenant creation".
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image) -
0
Hi @aaron
Yes, I mean the scope of my migration.
The possible triggers for these migrations are (as far as I can see at the moment - I might be overlooking some):
- Running the migration console app
- Tenant creation
But I see what you are saying - I think your suggestion might work on tenant record creation.
Thanks for this!
Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image)