Architecture
Overview
Antelopejs follows an interface-based architecture built on three core concepts: Interfaces, Modules, and the Core. Together, they create a system where components are loosely coupled, independently replaceable, and easy to reason about.
Interfaces — Contracts Between Modules
An interface defines a contract: a set of functions, events, or registrations that a module provides to the rest of the application. Other modules import and call these interfaces without knowing which module implements them.
This separation is the foundation of loose coupling in Antelopejs. A module that needs database access imports the database interface — it never imports the database module directly. The core resolves which module fulfills the contract at runtime.
Module A exports Interface X, defining the contract. Module B imports Interface X and calls its methods. Module B has no dependency on Module A — only on the interface.
Modules — Building Blocks
Each module is an independent package that exports and/or imports interfaces. Modules are self-contained: they have their own dependencies, configuration, and lifecycle.
Every module follows the same lifecycle:
- Construct — The module initializes its internal state
- Start — The module begins serving its interfaces
- Stop — The module stops serving and cleans up active work
- Destroy — The module releases all resources
Each lifecycle function maps to a specific state transition:
| Function | Transition |
|---|---|
construct() | loaded → constructed |
start() | constructed → active |
stop() | active → constructed |
destroy() | constructed → loaded |
Because modules only depend on interfaces, you can replace any module with another that implements the same interfaces. The consuming modules keep working without any code changes.
Both the Stripe module and the Ogone module implement the Payment interface. The Checkout module consumes the Payment interface. Swapping Stripe for Ogone requires only a configuration change — the Checkout module remains untouched.
The Core — The Conductor
The core is minimal by design. It handles three responsibilities:
- Module lifecycle — Loading, starting, stopping, and destroying modules in the correct order
- Interface routing — Connecting interface producers to consumers automatically
- Runtime management — Graceful shutdown, Hot Module Reloading (HMR) orchestration, and error propagation
The core does not contain business logic. Instead, the core acts as the conductor: it makes sure modules start in the right order, interfaces find their implementations, and failures propagate cleanly.
Benefits
This architecture delivers practical advantages across the entire development lifecycle:
- Consistent patterns — Every module follows the same structure and lifecycle, so onboarding is fast and code reviews are predictable
- Swap without rewriting — Change your database, payment provider, or notification service by swapping one module for another. Dependent code stays the same because the interface contract holds
- Predictable contracts — Interfaces define stable boundaries between modules. Changes to an interface are explicit and intentional, keeping dependent modules safe
- Live development updates — HMR gives you immediate feedback during development. Change a module, save the file, and the updated code loads automatically
Error Handling
Each lifecycle phase propagates errors differently.
construct() — Errors propagate to the module manager. The resolver detour detaches on failure. Modules construct in parallel via Promise.all, so other modules may continue constructing, but the first rejection causes the overall boot to fail.
start() — This function is synchronous with no internal try/catch. An exception propagates immediately and prevents remaining modules from starting. The system may end up in a mixed state where some modules have started and others have not.
stop() / destroy() — Errors propagate similarly. These functions can be async or sync. A failure during teardown does not automatically roll back other modules that have already stopped or been destroyed.
See also
- Modules — Module states and lifecycle details
- Creating a Module — Practical guide to implementing lifecycle functions