Alepha uses a Service Locator pattern wrapped in a strictly typed container. If you come from Java (Spring) or Angular, this will feel familiar. If you come from Express, this might feel new.
$inject)In Alepha, you don't use constructor(private service: Service). You use $inject.
1import { $inject } from "alepha"; 2import { Database } from "./Database"; 3 4class UserService { 5 // Alepha resolves this dependency lazily when the app starts 6 db = $inject(Database); 7 8 async get(id: string) { 9 return this.db.users.findById(id);10 }11}
Let's say you have an EmailProvider.
1export class EmailProvider {2 async send(to: string, body: string) {3 // Send via SMTP4 }5}
In development, you don't want to spam real emails. You want to log them to the console.
1export class ConsoleEmailProvider extends EmailProvider {2 async send(to: string, body: string) {3 console.log(`[EMAIL TO ${to}]: ${body}`);4 }5}
In your main.ts:
1const app = Alepha.create(); 2 3if (process.env.NODE_ENV === 'development') { 4 // Magic! 5 // Everywhere 'EmailProvider' is injected, 'ConsoleEmailProvider' will be used instead. 6 app.with({ 7 provide: EmailProvider, 8 use: ConsoleEmailProvider 9 });10}11 12run(app);
Services in Alepha have a lifecycle. You can hook into it using $hook.
configure: Setup configuration, read env vars.start: Connect to databases, open ports.ready: App is live, start cron jobs.stop: Graceful shutdown, close connections. 1class Database { 2 onStart = $hook({ 3 on: "start", 4 handler: async () => { 5 console.log("Connecting to DB..."); 6 await this.client.connect(); 7 } 8 }); 9 10 onStop = $hook({11 on: "stop",12 handler: async () => {13 await this.client.close();14 }15 });16}
You don't call these manually. run(alepha) handles the orchestration for you.