arjun289

arjun289

Architecture to allow creation of third party plugins for an E-Commerce App

I am working on this particular E-Commerce application, it’s a fully open source project.

It’s an umbrella with three apps:

  • A core app to handle logic and db related things.
  • An api app to expose APIs for customer frontend client.
  • A phoenix app for the admin interface.

The next phase of our development is to design the system in a manner that it’s very simple for people to create plugins or extensions. Also, the design to integrate the plugins should be such that the admin has the flexibility to add or remove them from the admin interface.

It is similar to how Shopify allows installation of extensions. We don’t fully understand how extension installation works for individual sellers.

e.g.
Let’s say the admin wants to install a library to provide support for marketing in the system he/she should be able to install the plugin created for the purpose.
What this plugin would be capable of?

  • Track User login and send a welcome email.
  • Check for abandoned carts etc.

The plugin should be created in a manner that once it is installed it should be able to track the call to session controller's login` action and send a ping to the third party service. This can be done by somehow injecting code which comes from the extension. e.g. making a specific api call to third-party service.

Constraints:

  • Can’t include the library in mix.exs as the number of packages can be many.
  • Since this is a multitenant app these installations are also seller specific, every seller can have a unique combination of plugins.
  • We can not have a central

One of the approaches is to inject a piece of code in the controller action to be tracked, to send an event to another library which based on metadata that the event sent(which includes a list of plugins for that particular seller) makes a decision and makes calls to the respective third party services.

What could be a possible design solution to provide the functionality?

Most Liked

edwildgoose

edwildgoose

I think this requirement is quite common and desirable.

For example lets imagine I wanted to build an email server to replace my current usage of postfix/sendmail/etc. Or I want an XMPP server (to pick an example where we can observe a plugin architecture on a high quality app)

In all these cases it’s desirable to ship the app compiled and then have the customer be able to configure it as dynamically as possible for their use case, possibly including optional functionality that they may write/create themselves

Part 1)
The lack of mix requirement seems workable because one can load any beam files you find and I guess it would be possible to dynamically start included applications if they conform to some interface. This would allow our main app to startup dependent apps which offer some kind of very dynamic plugin functionality

An alternative would be to relax the contraint on recompiling the main app and include code in the main app to start your plugins and include their code

Part 2)
You need some kind of hook architecture to allow your main app to call your plugins. This seems to be where you get furthest away from the Elixir way to do things. (Meaning that Elixir encourages everything to be explicit and it discourages clever tricks involving side effects, eg message bus and event handler solutions)

Simplest is explicitly passing hook functions to key modules. eg a module offering http client functionality could pass a callback module which implements the relevant hook code. Disadvantage of this technique is that it doesn’t scale to running multiple modules of hooks and must be defined quite explicitly in the calling code (would be more difficult to make dynamic)

Probably next up would be a plug style architecture, so that you very explicitly include plugin functionality into obvious hook functions, however, it’s much easier to specify callback chains and create a clear way for callbacks to veto further callbacks getting executed.

Next up would be that the hooks are maintained in a run time variable, so for example think gen_event or something you build yourself from genservers, genstage, etc. Dynamically things can start to listen to your hooks or remove themselves. This now allows external code to decide to compose or decompose itself from your main app. eg some config file could decide to attach some functionality to a hook. Also now you have the ability to run up multiple instances of a module and hook it to multiple hooks, eg in our mail server we might write some plugin functionality to decide if we accept/reject some email address as valid, that same module can be hooked into the hook to check both receiver address and the sender address, etc.

Finally I guess you can totally distribute the app and even remove the requirement to start all the code from the same startpoint using some kind of event bus, eg elixir registry, gproc, elixir pub/sub, kafka, etc. This has some challenges in not starting work before all plugins are attached and configured correctly

I think probably plug and mongoose/ejabberd are some good examples of how one can create a plugin architecture.

svarlet

svarlet

It sounds like you are trying to implement something generic and flexible for all sort of usecases.

Why don’t you start by creating one such plugin? Then a second? Then a third?

This will give you insight such as:

  • is our current architecture making it hard to create plugins?
  • what’s the commonalities between the first and second plugins? with the third?

You’ll certainly get somewhere, it may not be perfect but you can always rework some bits as you go. Trying to make something lile this right in one go will either lead to analysis paralysis or something unmaintainable.

notriddle

notriddle

The big alternative to using a multi-server system (which is what @dimitarvp’s idea of using an event bus would mean) is to use luerl to allow “scripting” your application the same way JavaScript allows you to script the browser and VBA allows scripting the Microsoft Office apps.

Where Next?

Popular in Questions Top

hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
mgjohns61585
Could someone help me? I’m making my first elixir program, number guessing game. I can’t figure out how to convert the user’s guess from ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
Qqwy
Update: How to use the Blogs & Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 127089 1222
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement