Hey there! I’m looking to get the community’s thoughts on deploying a combination of an open source application in combination with closed source additions. 99% of the application is open-source, ready to be self-hosted by anyone. The remaining 1% would be closed source to support a SaaS version of the application.
Microservices: The closed source additions would be separate services
that communicate with the main application via APIs. This would allow
the main application to remain open source, while the closed source
additions could be hosted on a separate server. The downside is that
maintenance, testing, and deployment would be more complex than
necessary.
Injecting files: The closed source additions would be injected into
the main application at build time. This would allow the main
application to remain open source, while the closed source additions
would be included in the same deployment. I’ve never seen this done
so I’m not sure how feasible it is, or if it would introduce some
maintenance headaches.
Umbrella application: I could structure the application as an umbrella
application, with the main application and the closed source additions
as separate applications under the same umbrella. At build time, the
closed source additions would be included. I’ve never worked with
such a structure before, so I’m not sure how feasible it is.
Do you have any experience with deploying an elixir with the combination open-source + closed source parts? I would appreciate any feedback or help.
Why not simply two separate applications, where the closed source one depends on the open source one and builds a release with both? No need for an umbrella for that approach.
Extremely simple, either use the dependency directly from your git with an authorized keypair or have a private package on hexpm, then you can simply use an authorization token to fetch it.
That would mean that the open-source CI pipeline fetches the private repo/hex package during its runtime, and uses it for testing, right? Aren’t you exposing the private repository into the public in that case?
Highly depends on how you package. As @LostKobrakai mentioned already, you can have the OSS application and the private one containing your OSS + closed-source code.
Think of it the same as how Oban is. The OSS version is completely open, then the pro version uses the functionality of the OSS one + additional closed-source.
Reporting back (for other curious readers) that I’ve started an experiment as suggested to include the main application as a dependency. I’ve added the main application as a git submodule, and added it to the dependency list of wrapper project.
It didn’t go very smoothly, but I haven’t yet given up. Here are some problems:
The original application declares quite a lot of configuration options in its config/config.exs. Unfortunately, this is ignored by the wrapper application. So I would need a way to share the configuration options between the two apps.
CI is not trivial. The majority of development happens in the original application, and every commit is fully tested. The private parts can’t be pulled as part of the open-source pipeline because it would expose the source code (and then what’s the point in splitting it?), so my best answer to this is to write contract tests in the original app, and make sure to not break the API that is the connection point between the app and the admin interface.
The original is meant to be released as a docker image. The wrapper needs to maintain a similar docker build. This is achievable, but some duplication seems to be necessary.
I also got some good ideas from Sharing code across apps. Continuing forward and reporting back how it goes.
I wanted to share an update on the experiment I mentioned earlier, where I tried including the main application as a dependency. After spending some time wrestling with it, I decided to scrap the idea and go in a different direction. Here’s how it played out.
The original approach involved adding the main application as a Git submodule and listing it as a dependency in the wrapper project. While it worked in theory, the practice was a mess. Configurations in config/config.exs didn’t carry over cleanly, which meant I had to figure out how to share them between apps. CI pipelines were even trickier since the main application is private, and I couldn’t just pull it into the open-source pipeline without exposing the source code. Then there was the duplication in Docker builds. It was too much pain for too little gain.
So I took a step back to rethink the whole setup. Turns out, the admin panel didn’t need to be its own project. We moved it into the main repository under an ee/ directory, where it could live alongside the rest of the app. To keep the licensing clear, we added a note that the ee parts are under a different license, while the main app stays Apache-2.
The hardest part of the transition was restructuring the asset pipeline. We had to move it to the root of the project so it could handle both the standard assets and the ones in the ee/assets directory. It took some work, but now it’s all in one place and working as it should.
This new setup is simpler, easier to maintain, and feels a lot better to work with. Sometimes the right solution is the one that looks obvious in hindsight. Thanks to everyone who shared advice and ideas earlier. It made all the difference.