How to build a project that supports module architecture in Phoenix?

I have a web project in Phoenix. It’s the main project, the bare minimum and let’s refer to it as P1. Say, it’s a blog, for simplicity. And there’re 100 customers that use P1 and every one wants to adjust, slightly, a copy of P1 to his needs.

I want to be able to replicate it thus creating, say, 100 copies of it. All the copies will be very similar to P1 in the terms of the code, yet each one will have around 10% of custom code, settings and unique features. These could be: additional fields in a database and model, additional route, modified html code on some pages or navidation menu or footer on the html pages, additional controller, css styles, js code, etc.

Important:

I’ll continue working on P1 and improve it. And it want to be able to incorporate the improvements into the projects-copies via git, into all the hundred, without having to deal with the hell of merging the code into each one. The code of P1 is the base and has the priority over the code of any child project.

Using branches for that, a branch per project, in git won’t work well.

Question: How should one go about building this type of architecture?

variant 1: adding new or modifying existing Elixir code in the child projects is optional because otherwise it’d be difficult to deal with.

There are a few things needed because of that:

  • P1 is not allowed to have breaking changes in behavior
  • P1 and their variants need protocols to prevent conflict in naming (modules, map keys, enums, db columns) wherever the variants are allowed to extend P1.

This might still not mean you don’t need to merge things at all. It should just mean updating P1 is always possible without conflict. But e.g. moving a custom feature of a variant later to a now core behavior of P1 will need you to move things over.

Generally this feels like a kind of CMS or similar project. You can look at the various ones out there, how they’re dealing with it.

1 Like

I think you need some sort of plugin architecture. In this case you would have your core logic in which it is improved by you over time and no customer code can change it. When a customer needs to change some functionality, he must add a plugin or change the code that is not part of the main logic (probably templates which includes html, css and other pages).

The plugins would have hooks that would be activated at installation (add a column in the database, change a template, etc.). As you gave an example of a blog, Wordpress itself uses this architecture to improve its functionality.

About routing, I think it’s worth creating another module that abstracts the phoenix’s routing for the custom customer routes. Phoenix router will call this module (basically a loop on this routes) so that your customers cannot change the core behavior (your own core routing with internal the controllers).

How to create a simple plugin? Namely, all the plugins, be it zero or N amount of them, and the amount of the them is unknown beforehand, will have to be found dynamically at start of an app and loaded dynamically too, at runtime.

I probably gave the wrong idea when I mentioned the plugin architecture, it just a way to add new functionality to an application without changing the original code from the core module (and I think that’s your main problem).
In Elixir you can separate the core application from the customer-specific logic. The communication between those two could be through an adapter module, which your phoenix app would consume and understand what to do. I was thinking too much on how Wordpress do when I mentioned the installation part (sorry for the confusion).
In this adapter you can map the customer’s new migrations, routes, controllers and views. The core app will need a generic way read this adapter and incorporate its functionality (similar to a plugin architecture).

Plugins will work. How to create a simple one?

And how to build an adapter module also?

It’s simple as mix new, with or without --sup

I’ve run “mix new”, a plugin hasn’t been created.

mix new <plugin_name> [--sup]

Any plugin is just a mix project.

A monk asked Zhao Zhou to teach him.
Zhao Zhou asked, “Have you eaten your meal?”
The monk replied, “Yes, I have.”
“Then go wash your bowl,” said Zhao Zhou.
At that moment, the monk was enlightened.

3 Likes

Dear Romik, welcome to this forum!
Btw, you’re just arrived and already do make a fuss? Please give yourself the time to get to know who you are yelling at. There is no shame in being wrong and there is so much to learn from.

3 Likes

I see You don’t like my short answers…

But there is not much more than this.

$ mix phx.new koko1
$ mix phx.new koko2
$ mix new ohmyplugin

Use ohmyplugin in both koko1 and koko2, in mix.exs, as path dependency and You will share this plugin in both Phoenix applications.

You can put whatever You like in ohmyplugin… just add all dependencies You need.

I was here all along :slight_smile:

4 Likes

There is no magic command like mix phx.plugin. You need to learn something about software architecture (microkernel pattern). And then write it yourself.

2 Likes

To be fair: Creating a new mix project is not a plugin system. Composing applications via dependencies is the “pulling in code” part (needs recompiling, too), but not the “register code to be called” or “compose code so plugin code can hook in” part. A complete plugin system needs a bunch more than ways to pull in dependencies. It’s even more complex if things shall be composed at runtime.

2 Likes

Thank you for the link! Haven’t read this book yet.

1 Like