Modern Django applications often start small.
But as the platform grows, developers begin facing the same problems:
- Repeated URL registrations
- Manual signal imports
- Scattered Celery configurations
- Frontend asset duplication
- Tight coupling between apps and the core project
At Horilla Open Source HRMS & CRM, we wanted a more scalable approach for building modular enterprise applications in Django.
Instead of treating Django apps as isolated modules, we wanted apps to behave like plugins that could automatically integrate themselves into the platform.
That led to the creation of:
AppLauncher — Horilla’s Plugin Architecture Layer for Django
AppLauncher is a reusable extension of Django’s AppConfig that enables:
- Automatic URL registration
- Plugin-style app loading
- Auto-discovery of signals and menus
- Dynamic JavaScript asset registration
- Celery schedule integration
- Modular HRMS and CRM architecture
- Scalable enterprise Django development
This architecture helps Horilla function not only as an HRMS platform, but also as an extensible Django ecosystem.
Why Traditional Django App Wiring Becomes Difficult at Scale
In a standard Django project, every new app usually requires manual integration.
For example:
Manual URL Registration
urlpatterns = [
path("crm/accounts/", include("horilla_crm.accounts.urls")),
]
Manual Signal Imports
class AccountsConfig(AppConfig):
def ready(self):
import horilla_crm.accounts.signals
Manual Celery Schedule Registration
CELERY_BEAT_SCHEDULE.update(...)
Manual JavaScript Injection
<script src="/static/accounts/app.js"></script>
This approach works for small projects.
But in large-scale systems like:
- HRMS platforms,
- CRM software,
- ERP systems,
- SaaS applications,
- multi-module Django platforms,
manual wiring creates:
- duplicated setup,
- maintenance overhead,
- tight coupling,
- onboarding complexity,
- scalability problems.
The Goal: Build a Plugin-Based Django Platform
At Horilla, we wanted new apps to behave like installable plugins.
Meaning:
- apps should self-register,
- modules should auto-discover themselves,
- frontend assets should load dynamically,
- and the core platform should remain untouched.
Instead of this workflow:
“Add your app manually to multiple core files.”
We wanted:
“Install the app and it automatically integrates with the platform.”
This is the foundation of scalable plugin architecture in Django.
Introducing AppLauncher
Horilla introduces a reusable base class:
class AppLauncher(AppConfig):
Every modular app extends this class instead of directly inheriting from Django’s AppConfig.
Example:
class AccountsConfig(AppLauncher):
name = "horilla_crm.accounts"
url_prefix = "crm/accounts/"
url_module = "horilla_crm.accounts.urls"
url_namespace = "accounts"
js_files = "accounts/assets/js/accounts.js"
auto_import_modules = [
"registration",
"signals",
"menu",
"dashboard",
]
That is enough to integrate the app into the Horilla platform.
No:
- root URL modifications,
- manual imports,
- repetitive startup logic,
- duplicated initialization code.
How Horilla’s AppLauncher Works Internally
When Django starts, it loads installed applications and calls:
ready()
Horilla extends this lifecycle hook:
def ready(self):
self._register_urls()
self._register_js()
self._auto_import_modules()
self._register_celery_schedule()
This creates automatic platform-level integration for every app.
1. Automatic URL Registration in Django
Apps define:
url_prefix = "crm/accounts/"
url_module = "horilla_crm.accounts.urls"
Horilla dynamically injects URLs into the root URL configuration:
urlpatterns.append(
path(
self.url_prefix,
include(self.url_module),
)
)
Benefits
- No manual
urls.pyedits - Reduced coupling
- Easier app installation
- Better modularity
- Cleaner large-scale architecture
This is especially useful in enterprise Django platforms with many independent modules.
2. Automatic Module Discovery
Apps declare modules for automatic loading:
auto_import_modules = [
"signals",
"menu",
"dashboard",
]
Horilla dynamically imports them:
importlib.import_module(f"{self.name}.{module}")
Why This Matters
This enables:
- automatic signal registration,
- dynamic menu loading,
- dashboard widget discovery,
- plugin lifecycle initialization.
If an app contains:
signals.pymenu.pydashboard.py
Horilla automatically activates them.
This follows the powerful software design principle:
Convention Over Configuration
Instead of manually configuring everything, the framework follows predictable conventions.
This approach is used in:
- Ruby on Rails
- Laravel
- VSCode extensions
- browser plugin systems
3. Dynamic JavaScript Asset Registration
Apps can contribute frontend assets declaratively:
js_files = [
"accounts/assets/js/accounts.js"
]
Internally:
register_js(self.js_files)
This allows Django apps to inject frontend behavior without modifying shared templates.
Advantages
- Cleaner frontend architecture
- Better HTMX/JavaScript modularity
- Reduced template duplication
- Easier feature isolation
- Dynamic UI extensibility
4. Automatic Celery Beat Schedule Integration
Large enterprise systems often require background jobs.
With Horilla, apps can ship their own Celery schedules:
celery_schedule_module = "celery_schedules"
Horilla automatically merges:
HORILLA_BEAT_SCHEDULE
into:
settings.CELERY_BEAT_SCHEDULE
Benefits
- Decentralized task management
- Better app ownership
- Cleaner background job architecture
- Easier scaling of scheduled services
Why AppLauncher Changes Django Architecture
The biggest architectural shift is this:
Traditional Django Architecture
Core project controls apps
Horilla Plugin Architecture
Apps extend the platform themselves
This transforms Django from:
- a collection of apps
into:
- a scalable modular platform.
Building Scalable HRMS & CRM Systems with Django
Horilla uses this architecture to build:
- HRMS modules,
- CRM modules,
- attendance systems,
- employee management,
- recruitment systems,
- payroll integrations,
- enterprise workflows.
Because apps are plugin-oriented, new business modules can integrate into the platform with minimal core modifications.
This dramatically improves:
- maintainability,
- developer experience,
- scalability,
- onboarding,
- long-term architecture health.
Plugin Architecture in Django: Key Benefits
Using AppLauncher, a new app can automatically provide:
- URLs
- signals
- menus
- dashboards
- APIs
- Celery jobs
- frontend assets
- startup hooks
without modifying the core platform.
This creates:
- self-contained Django apps,
- reusable enterprise modules,
- cleaner platform architecture,
- extensible SaaS foundations.
Framework Thinking vs Traditional App Thinking
Many Django projects focus only on:
- models,
- views,
- templates.
But large-scale platforms require:
- extension points,
- lifecycle hooks,
- auto-discovery systems,
- registries,
- plugin loaders,
- modular conventions.
AppLauncher became one of the core abstractions that helped Horilla evolve into a modern modular HRMS platform built with Django.
Final Thoughts
Django already provides excellent architectural foundations through AppConfig.
But small abstractions can create massive long-term improvements.
By extending Django with plugin-style architecture patterns like AppLauncher, developers can build:
- scalable enterprise platforms,
- modular SaaS systems,
- reusable HRMS applications,
- extensible CRM ecosystems,
- maintainable large-scale Django projects.
For Horilla, AppLauncher became one of the foundational building blocks enabling platform-level scalability.
About Horilla
Horilla Open Source is a modular Django-based HRMS & CRM and enterprise management platform focused on:
- scalability,
- extensibility,
- modern UI architecture,
- HTMX integration,
- plugin-based modularity,
- enterprise workflow management.
Explore Horilla
- GitHub: Horilla GitHub Repository
- Website: Horilla Official Website
- Dev Community: Horilla on Dev.to
- LinkedIn: Horilla LinkedIn Page
If you are building large Django applications, modular HRMS systems, CRM platforms, or enterprise SaaS products, plugin-style architecture can significantly improve maintainability and scalability.
Top comments (0)