Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discussion] - Folder Structure #2

Open
dazinator opened this issue Jun 28, 2016 · 7 comments
Open

[Discussion] - Folder Structure #2

dazinator opened this issue Jun 28, 2016 · 7 comments

Comments

@dazinator
Copy link

dazinator commented Jun 28, 2016

I generally like this mechanism, nice work.

It's a bit different from my own approach but I like your solution because it solves the issue of not having to worry about copying content to the applications output Directory on build. This means you get nice in-place editing of files too because you can use PhsyicalFileProviders over the static content directly in the projects wwwroot folder on disk.

The only weakness is that it requires you to structure your source code in a certain way, i.e all Modules in a Modules folder. This also means when deploying your application, you have to remember to publish that folder too - which will contain source code - necessitating in the use of the "exclude" attribute.

I am far down the path of a slightly different approach at the moment, where the application uses DependencyContext to find referenced Modules, and then wires up EmbeddedFileProvider's for static content, and razor files etc. This means you can structure the solution however you like, don't need to worry about "exclude" attributes in project.json, but have to use "embed" attributes in project.json instead, to embed static content / Views folder in your assemblies. The downfall of my approach is that this works great for a published application, but during development time, as static files are embedded, you lose the ability to edit them on the fly, you have to edit them and re-build to produce the newer assembly containing the modified content. This would be a massive pain when editing .js / html files for example, so I am now working on a workaround to that problem perhaps so that EmbeddedFileProvider are only used for a published / released app, and PhsyicalFileProviders are used at development time, pointing at the project's folders similar to this solution.

Anyway, thanks for this example, was a very simple way of achieving a modular result!

@thiennn
Copy link
Owner

thiennn commented Jul 4, 2016

Sorry for the late response, I have missed the notifications. Actually, you can structure your source code in any way that fix your need. It is ok to put the "Modules" folder outside the Modular.WebHost. The WebHost only need know where modules are located.

Yeah, I think we should write some automation tasks, inject into the publish pipeline to copy the build artifact of each modules (bin, Views, wwwroot) to published "Module" folder.

I am aslo thinking about a situation that in the admin side, there is a page that list all the modules (online), each one has the "Install" button. Click on this button will download the module put them to the right location and load them up (the app might need to be restarted)

@YodasMyDad
Copy link

I am aslo thinking about a situation that in the admin side, there is a page that list all the modules (online), each one has the "Install" button. Click on this button will download the module put them to the right location and load them up (the app might need to be restarted)

Did you get anywhere with this?

@thiennn
Copy link
Owner

thiennn commented Mar 1, 2017

Everything is ok except the database schema. For example if a modules need some changes in data schema (like add more tables, columns) to operate. I haven't found a good solution to address this.

@YodasMyDad
Copy link

Wouldn't EF 7 migrations take care of that?

@thiennn
Copy link
Owner

thiennn commented Mar 1, 2017

In EF ,7 the migration (CLI) has been separated from it's core, I haven't figured out how to generate and apply migration programmatically.

@dazinator
Copy link
Author

I went with a 2 part solution. I have a module that checks for and applies upgrade actions. I call it the module migrator module! An upgrade action is just a class that other modules can implement, that has some Up() method and a Down() method although I don't do rollbacks (it also has a GUID).The migrator module, in startup, scans all modules and gets all upgrade actions. It also adds a middleware to the http pipeline. The middleware, on first request, looks at the list of upgrade actions and looks at a database table to work out which ones (identified by GUID) have been applied and which ones are new. If there aren't any new upgrade actions it remembers this so it doesn't have to check on any future requests. If there are upgrade actions to execute, it can either (based on a setting) attempt to execute them (by looping through, activating, and calling the UP() method on each action, and then updating its database table to record successfully applied actions so they won't run again in future. Or it can redirect to a View that the module provides. The view will display a UI if you are an admin, listing the pending actions that need to be applied, with a button to apply them. If you are not an admin, the view just displays a public message saying the site is currently unavailable pending maintainence.

Now onto the actions themselves..

The actions can perform whatever upgrade functionality a module needs. I decided not to allow modules to share a dbcontext, but instead to implement their own dbcontexts, but they all target the same connection string provided by the core, so basically they all use the same database.

Where this gets tricky is if one module needs to extend or join to a table that is represented by an entity in another modules dbcontext. However you can do this by allowing that module to reference the other module, then it can define an entity inheriting the base entity, the in its dbcontext setup, it can configure that new entity to target the existing table. It's fiddely but I did manage to achieve it in a few places.

I did briefly look at having some mechanism of allowing each module to intercept the setup of a single / central dbcontext but I didn't get very far. Perhaps I'll relook at that approach in future.

When you have modules that extend another modules tables, there are certain kinds of changes that should be prohibited so as not to break the original module. For example adding a column that is not nullable and has no default value. If you made that kind of change, the original module would no longer be able to save changes to its table based in its configured entity model definition. However if you adhere to these rules it can work.

@dazinator
Copy link
Author

I need to approve my approach though so that it runs upgrades in dependency tree resolution order. That's to say, if module A depends on module B and they both depend on Module C, I need to resolve the dependency tree so that upgrades are applied in the order module C then (module B & module A).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants